мой блогПожалуйста, указывайте первоисточник при перепечатке.
последовательность
Когда я впервые узнал о концепции инверсии управления, я изучалSpring
время кадра.IOC
иAOP
в видеSpring
Две основные характеристики, естественно, мы должны усердно учиться. Dependency Injection (DI) давно меня озадачил, и я не понял связи между ними.
Инверсия контроля
Инверсия управления, как следует из названия, инвертирует управление, так какие же элементы управления инвертируются? в 2004 годуMartin fowler
Бог предложил
«Какие аспекты контроля были изменены?»
Проблема, заключил он, заключается в том, что приобретение зависимого объекта происходит в обратном порядке.
При разработке принципа единой ответственности существует несколько задач, которые может выполнить один объект. Для выполнения большинства задач требуется взаимодействие нескольких объектов, поэтому между объектами существуют зависимости. В начале сами решаются зависимости между объектами, и какие объекты нужныNew
Один вышел из употребления, управление находится в самом объекте. Однако степень связанности таким образом очень высока, и небольшая модификация объекта может вызвать цепную реакцию, и зависимые объекты необходимо модифицировать полностью.
Если получение зависимого объекта отменяется, то, что и когда генерируется зависимый объект, определяется внешним объектом.IOC容器
принимать решение. Поскольку объект может быть получен при использовании зависимого объекта, обычно используемые методы включают внедрение зависимостей и поиск зависимостей (поиск зависимостей). Таким образом, связь между объектами и объектами удаляется из объекта, и нет необходимости изменять исходный код, даже если в будущем будет модификация зависимости.
Подводя итог, инверсия управления относится к передаче управления зависимостями объекта от внутреннего к внешнему.
внедрение зависимости
Инверсия управления относится к внешнему управлению зависимостями между объектами, но зависимости относятся к внешней стороне объекта, и сам объект все еще должен использовать зависимый объект.В это время используется внедрение зависимостей. Как следует из названия, приложению необходимо внедрить зависимости, необходимые объекту, извне. Его можно внедрить через конструктор объекта, который называется构造器注入(Constructor Injection)
. если черезJavaBean
Внедрение параметра метода атрибута называется设值方法注入(Setter Injection)
.
Независимо от того, как он вводится, если мы введем его вручную, это все равно будет слишком хлопотно. В настоящее время нам нужен контейнер, который поможет нам реализовать эту функцию и автоматически внедрить зависимости, требуемые объектом.Этот контейнер упомянут выше.IOC容器
.
Взаимосвязь между инверсией управления и внедрением зависимостей также очевидна: можно сказать, что они по существу одинаковы, но конкретные проблемы различны. В центре внимания инверсии управления находится передача прав управления, в то время как внедрение зависимостей содержит смысл инверсии управления, который ясно описывает, что зависимый объект управляется извне, а затем внедряется в объект. Реализовано внедрение зависимостей, а управление реверсировано.
пример
- Первый - традиционный способ, связь очень серьезная.
public class Main {
public static void main(String[] args) {
OrderService service = new OrderService();
service.test();
}
}
public class OrderService {
private OrderDao dao = new OrderDao();
public void test() {
dao.doSomeThing();
}
}
public class OrderDao {
public void doSomeThing() {
System.out.println("test");
}
}
- Далее идет способ без использования контейнера, слабая связь, но ручная инъекция очень хлопотна.
public class Main {
public static void main(String[] args) {
Dao dao = new OrderDao();
OrderService service = new OrderService(dao);
service.test();
}
}
public interface Dao {
void doSomeThing();
}
public class OrderDao implements Dao {
@Override
public void doSomeThing() {
System.out.println("test");
}
}
public class OrderService {
private Dao dao;
public OrderService(Dao dao) {
this.dao = dao;
}
public void test() {
dao.doSomeThing();
}
}
- Далее используйте контейнеры на благо человечества.
// 引导类要放在项目根目录下,也就是在 src 下面
public class Main {
public static void main(String[] args) {
// 生成容器
Container container = new Container(Main.class);
// 获取Bean
OrderService service = container.getBean(OrderService.class);
// 调用
service.test();
}
}
@Component
public class OrderService {
@Autowired
private Dao dao;
public void test() {
dao.doSomeThing();
}
public Dao getDao() {
return dao;
}
public void setDao(Dao dao) {
this.dao = dao;
}
}
@Component
public class OrderDao implements Dao {
@Override
public void doSomeThing() {
System.out.println("test");
}
}
public interface Dao {
void doSomeThing();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Component {
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface Autowired {
}
public class Container {
private List<String> classPaths = new ArrayList<>();
private String separator;
private Map<Class, Object> components = new HashMap<>();
public Container(Class cls) {
File file = new File(cls.getResource("").getFile());
separator = file.getName();
renderClassPaths(new File(this.getClass().getResource("").getFile()));
make();
di();
}
private void make() {
classPaths.forEach(classPath -> {
try {
Class c = Class.forName(classPath);
// 找到有 @ioc.Component 注解的类并实例化
if (c.isAnnotationPresent(Component.class)) {
components.put(c, c.newInstance());
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
});
}
/**
* 注入依赖
*/
private void di() {
components.forEach((aClass, o) -> Arrays.stream(aClass.getDeclaredFields()).forEach(field -> {
if (field.isAnnotationPresent(Autowired.class)) {
try {
String methodName = "set" + field.getType().getName().substring(field.getType().getName().lastIndexOf(".") + 1);
Method method = aClass.getMethod(methodName, field.getType());
if (field.getType().isInterface()) {
components.keySet().forEach(aClass1 -> {
if (Arrays.stream(aClass1.getInterfaces()).anyMatch(aClass2 -> aClass2.equals(field.getType()))) {
try {
method.invoke(o, components.get(aClass1));
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
});
} else {
method.invoke(o, components.get(field.getType()));
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}));
}
/**
* 该方法会得到所有的类,将类的全类名写入到classPaths中
*
* @param file 包
*/
private void renderClassPaths(File file) {
if (file.isDirectory()) {
File[] files = file.listFiles();
Arrays.stream(Objects.requireNonNull(files)).forEach(this::renderClassPaths);
} else {
if (file.getName().endsWith(".class")) {
String classPath = file.getPath()
.substring(file.getPath().lastIndexOf(separator) + separator.length() + 1)
.replace('\\', '.')
.replace(".class", "");
classPaths.add(classPath);
}
}
}
public <T> T getBean(Class c) {
return (T) components.get(c);
}
}
постскриптум
Некоторые понятия всегда кажутся мне ясными, но когда я использую их или записываю, я обнаруживаю, что многого не понимаю. Цель этой статьи — разобраться в концепциях и сделать некоторые заметки. В этот раз попробовал самIOC容器
, я с самого начала знал, что у меня проблемы с прежним пониманием. В конце концов, я написал удобную версию для работы с примерами в статье. Вы можете обратиться к нему позжеSpring
реализации, считается, что можно многому научиться.