问题
Let's say that we use the @Autowired
annotation over various fields in a class, and that we didn't write setters or constructors that can also set the fields.
Question - what should the access modifier be, private
or package-private
(i.e. none) ?
For example:
public class MyClass {
@Autowired
private MyService myService;
}
vs
public class MyClass {
@Autowired
MyService myService;
}
In the first case (private
fields) Spring uses reflection to wire up the field, even if it doesn't have a setter.
The second case (package-private
fields) allows us to be able to access those fields (for example, to set up mocks) if we need to extend the class for testing purposes.
So both cases work fine, but which is more recommended, particularly with regards to testing?
回答1:
The first case also allows you to inject mocks depending on the framework. For example using the @InjectMocks
annotation of Mockito. You also have ReflectionTestUtils.setField
in Spring test, ...
I'm personally not too fond of modifying classes too much for testing purposes, so I would go for the first case. But at the end of the day this mostly depends on your preferred test framework.
回答2:
I generally prefer having the field private and using setter injection:
public class MyClass {
private MyService myService;
@Autowired
public void setMyService(MyService myService) {
this.myService = myService;
}
}
allowing the service to be @Autowired, but set with a mocked instance for unit testing.
回答3:
So both cases work fine, but which is more recommended, particularly with regards to testing?
I think the properties should be private
:
@Autowired
private MyService myService;
As it is always good to have getter methods to provide access to the properties instead of allowing other classes to have direct access to them.
And for testing purposes, injection of mocks
of private properties
will work the same way as that of package-private
properties.
For example, with Mockito
, you can inject a mock of private MyService
into MyClass
as this:
public class MyClassTest {
@Mock
MyService service;
@InjectMocks
MyClass serv = new MyClass();
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
}
回答4:
I would generally NOT use @Autowired for private fields or methods. @Autowired means, somebody from outside will set this field. "Private" on the other hand means nobody except this class is allowed to use it.
Mixing @Autowired and private can theoretically cause problems, if the JIT compiler somehow optimizes this code. It can be a Java Memory Model concurrency related problem, which will be production-only and impossible-to-reproduce.
I would make the Autowired fields at least package visible. As free bonus it will allow to write unit tests without tricks and workarounds.
回答5:
I would rather use private on @Autowired fields, for a few reasons:
- I use these fields for dependency injection, generally in service classes, to use other service classes. In that case I want to keep those fields to the current class.
- Also, extending this class can lead to different logic, so maybe another implementation of the @Autowired fields is needed, hence the private instead of package-private.
- Furthermore, when refactoring, it helps to see when such a field is not used anymore, as package-private fields don't show a warning when unused (assuming your IDE is Eclipse - I actually don't know for other IDEs).
来源:https://stackoverflow.com/questions/19682293/spring-autowired-fields-which-access-modifier-private-or-package-private