精华内容
下载资源
问答
  • 依赖注入spring的一个特性,从配置层面解决了程序耦合、依赖问题,spring提供了构造函数依赖注入、Setter方法依赖注入、自动装配依赖注入和@autowired注解依赖注入等多种实现方式。 那么依赖注入是如何实现的?第...

    依赖注入是spring的一个特性,从配置层面解决了程序耦合、依赖问题,spring提供了构造函数依赖注入、Setter方法依赖注入、自动装配依赖注入和@autowired注解依赖注入等多种实现方式。

    那么依赖注入是如何实现的?第一反应就是java反射呗,比如构造函数注入,我们可以通过反射读取Bean类的构造函数,参数个数,参数类型,所以只要我们在xml配置文件中指定了参数类型或参数顺序就可以轻松通过反射创建出该Bean的实例。但是实践过程中我们发现,xml配置文件中指定name=参数名称也可以实现依赖注入,这是java反射很难实现的至少jdk1.8以下实现不了,因为jdk1.8以下通过反射得不到具体的参数名称。看一个案例。

    Student.java

    package com.marcus.spring.beans;
    
    public class Student {
    	private Integer age;
    	private String name;
    	private String gender;
    	
    	public Student() {
    	}
    	public Student(String name, String gender, Integer age) {
    		this.name = name;
    		this.age = age;
    		this.gender = gender;
    	}
    	public void setAge(Integer age) {
    		this.age = age;
    	}
    	public Integer getAge() {
    		return age;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public String getName() {
    		return name;
    	}
    	public String getGender() {
    		return gender;
    	}
    	public String toString() {
    		return String.format("{name: %s, gender: %s, age: %d}", this.name, this.gender, this.age);
    	}
    }
    

    xml配置文件 ioc-di-asm.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
       
       <!-- Definition for student bean -->
       <bean id="student1" class="com.marcus.spring.beans.Student">
       	<constructor-arg  value="student1" />
       	<constructor-arg  value="male" />
       	<constructor-arg  value="11" />
       </bean>   
       <bean id="student2" class="com.marcus.spring.beans.Student">
       	<constructor-arg  type="java.lang.Integer" value="22" />
       	<constructor-arg  value="student2" />
       	<constructor-arg  value="male" />
       </bean>
       <bean id="student3" class="com.marcus.spring.beans.Student">
       	<constructor-arg  type="java.lang.Integer" value="33" />
       	<constructor-arg  name="gender" value="female" />
       	<constructor-arg  value="student3" />	
       </bean>
    </beans>
    

    测试类 DIAsmApp.java

    public class DIAsmApp {
    	public static void main(String[] args) {
    		AbstractApplicationContext context = new ClassPathXmlApplicationContext("ioc-di-asm.xml");
    		
    		Student student1 = context.getBean("student1", Student.class);
    		System.out.println("student1: " + student1.toString());
    
    		Student student2 = context.getBean("student2", Student.class);
    		System.out.println("student2: " + student2.toString());
    		
    		Student student3 = context.getBean("student3", Student.class);
    		System.out.println("student3: " + student3.toString());	
    		
    		context.close();
    	}
    }
    

    输出结果

    student1: {name: student1, gender: male, age: 11}
    student2: {name: student2, gender: male, age: 22}
    student3: {name: student3, gender: female, age: 33}
    

    案例说明

    1、student1,可以用反射实现

    xml配置了3个constructor-arg,没有type、index、name等其它装饰,这种情况我们通过java反射可以成功创建实例,即找3个参数的构造函数,Student(String name, String gender, Integer age),然后按照constructor-arg出现顺序依次给构造函数参数赋值,并创建实例。

    2、student2,可以用反射实现

    xml配置了3个constructor-arg:
    <constructor-arg type=“java.lang.Integer” value=“22” />
    <constructor-arg value=“student2” />
    <constructor-arg value=“male” />
    第1个参数标记为Integer类型,其余2个只有value值,没其它标识,这种情况我们依然可以通过java反射成功创建实例。

    • 找到3个参数的构造函数,Student(String name, String gender, Integer age);
    • 读取第1个constructor-arg type=“java.lang.Integer”,构造函数中找到类型为Integer的参数,发现是第3个参数,则设置arg2 = 22
    • 读取第2个constructor-arg,构造函数为arg0,arg1未设置,先后顺序赋值,arg0 = student2
    • 读取第3个constructor-arg,arg1未设置,arg1 = male
    3、student3,java反射无法实现

    xml配置了3个constructor-arg:
    <constructor-arg type=“java.lang.Integer” value=“33” />
    <constructor-arg name=“gender” value=“female” />
    <constructor-arg value=“student3” />
    第1个参数标记为Integer类型,第2个参数设置了name,第3个参数没特殊标识,假设可以通过java反射实现,则实现步骤应该是:

    • 找到3个参数的构造函数,Student(String name, String gender, Integer age);
    • 读取第1个constructor-arg type=“java.lang.Integer”,构造函数中找到类型为Integer的参数,发现是第3个参数,则设置arg2 = 22
    • 读取第2个constructor-arg,构造函数为arg0,arg1未设置,先后顺序赋值,arg0 = female
    • 读取第3个constructor-arg,arg1未设置,arg1 = student3

    我们得到student bean为{name=female, gender=student3,age=33},姓名变成了性别,性别变成了姓名,不是我们想要的结果,java反射失败!

    那么spring依赖注入为何可以正常工作呢?原因是spring使用了asm框架,可以读取java类字节码,读取到构造函数的参数名称,如上文的student3配置,spring可以读取到第二个constructor-arg name="gender"对应构造函数的arg1,所以就可以正常工作了。
    ASM框架代码演示:

    AsmDemo.java

    package com.marcus.spring.aop;
    
    import java.lang.reflect.Constructor;
    
    import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
    
    import com.marcus.spring.beans.Student;
    
    public class AsmDemo {
    	public static void main(String[] args) throws ClassNotFoundException {
    		LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer 
    			= new LocalVariableTableParameterNameDiscoverer();
    		Class<?> forName = Class.forName(Student.class.getName());
    		Constructor<?>[] constructors = forName.getConstructors();
    		for (Constructor<?> constructor : constructors) {
    			String argNames = "";
    			String[] parameterNames = localVariableTableParameterNameDiscoverer
    					.getParameterNames(constructor);
    			for (String parameterName : parameterNames) {
    				argNames += argNames.length() < 1 ? parameterName : "," + parameterName;
    			}
    			System.out.println(argNames.length()<1 ? constructor.getName() + "()" 
    					: constructor.getName() + "(" + argNames + ")");
    		}
    		//output:		
    		//com.marcus.spring.beans.Student()
    		//com.marcus.spring.beans.Student(name,gender,age)
    	}
    }
    

    通过ASM框架,我们读取到了com.marcus.spring.beans.Student(name,gender,age)构造函数参数名称,继续student3 bean注入的实现步骤:

    student3在xml文件中,配置了3个constructor-arg:
    <constructor-arg type=“java.lang.Integer” value=“33” />
    <constructor-arg name=“gender” value=“female” />
    <constructor-arg value=“student3” />
    第1个参数标记为Integer类型,第2个参数设置了name,第3个参数没特殊标识,通过java反射+ASM框架,实现步骤应该是:

    • 找到3个参数的构造函数,Student(String name, String gender, Integer age);
    • 读取第1个constructor-arg type=“java.lang.Integer”,构造函数中找到类型为Integer的参数,发现是第3个参数,则设置arg2 = 22
    • 读取第2个constructor-arg name=“gender”,构造函数中找到名称为gender的参数,发现是第2个参数,则设置arg1=female
    • 读取第三个constructor-arg,arg0未设置,arg0 = student3

    我们得到student bean为{name=student3, gender=female,age=33},成功!

    小结

    spring框架广泛使用了ASM框架,我们可以从spring的jar包构成可以看出ASM对于spring的重要性。以3.2.5.RELEASE版本为例,ASM部分在spring-core-3.2.5.RELEASE.jar包,spring最核心的jar包中!AOP,cglib等都需要ASM读取、操作java类。

    展开全文
  • Spring依赖注入实现简单工厂模式

    千次阅读 2018-08-01 17:39:37
    Spring依赖注入实现简单工厂模式 背景 想写一个简单的策略模式+简单工厂模式的抽奖算法. 下面写的是一个利用传入的抽奖类型调用不同的增加抽奖次数的方法. 首先写一个抽象工厂AbstractLotteryType类: ...

    Spring依赖注入实现简单工厂模式

    背景

    想写一个简单的策略模式+简单工厂模式的抽奖算法.

    下面写的是一个利用传入的抽奖类型调用不同的增加抽奖次数的方法.

    首先写一个抽象工厂AbstractLotteryType类:

    public abstract class AbstractLotteryType {
        /**
            增加抽奖次数的方法
            {@see userId} 用户Id
         */
        public abstract void addLotteryNumber(String userId);
    }

    然后写几个抽奖类型

    DrawLotteryType

    public class DrawLotteryType extends AbstractLotteryType {
        @Override
        public void addLotteryNumber(String userId) {
            System.out.println("执行了DrawLotteryType的新增方法");
        }
    }

    GoldenLotteryType

    public class GoldenLotteryType extends AbstractLotteryType {
        @Override
        public void addLotteryNumber(String userId) {
            System.out.println("执行了GoldenLotteryType的新增方法");
        }
    }

    SingleLotteryType

    public class SingleLotteryType extends AbstractLotteryType {
        @Override
        public void addLotteryNumber(String userId) {
            System.out.println("执行了SingleLotteryType的新增方法");
        }
    }

    然后写出抽奖类型的简单工厂LotteryTypeFactory

    public class LotteryTypeFactory {
        private AbstractLotteryType lotteryType;
    
        public LotteryTypeFactory(String type){
            switch (type){
                case "SINGLE":
                    lotteryType = new SingleLotteryType();
                    break;
                case "DRAW":
                    lotteryType = new DrawLotteryType();
                    break;
                case "GOLDEN":
                    lotteryType = new GoldenLotteryType();
                    break;
                default:
                    throw new RuntimeException("没有该类型活动");
            }
        }
    
        public AbstractLotteryType newInstence(){
            return lotteryType;
        }
    }

    然后是抽奖类型的枚举类:

    public enum LotteryTypeEnum {
        SINGLE,
        GOLDEN,
        DRAW
    }

    测试代码:

    public class demo {
        public static void main(String[] args) {
            LotteryTypeFactory singleFactory = 
                    new LotteryTypeFactory(LotteryTypeEnum.SINGLE.name());
            singleFactory.newInstence().addLotteryNumber("3");
            LotteryTypeFactory goldenFactory = 
                    new LotteryTypeFactory(LotteryTypeEnum.GOLDEN.name());
            goldenFactory.newInstence().addLotteryNumber("3");
        }
    }

    运行结果:

    执行了SingleLotteryType的新增方法
    执行了GoldenLotteryType的新增方法

    问题

    刚准备写入工作中的代码时,想起来IOC本来就是一个容器,何必还要自己写一个工厂去生产.于是转换了思路,利用Spring当做一个工厂(当然其实Spring中一般用到的是单例),去管理我们的几个抽奖类型.

    实现

    首先写一个抽奖类型的接口ILottery

    public interface ILottery {
        void addLotteryTimes();
    }

    然后写两个抽奖类型事件这个接口

    SingleLottery

    @Component("SINGLE")
    public class SingleLottery implements ILottery {
    
        @Autowired
        private LotteryService lotteryService;
    
        @Override
        public void addLotteryTimes() {
            System.out.println("调用了SingleLottery的addLotteryTimes方法");
            lotteryService.addLotteryTimes();
        }
    }

    GoldenLottery

    @Component("GOLDEN")
    public class GoldenLottery implements ILottery {
    
        @Autowired
        private LotteryService lotteryService;
    
        @Override
        public void addLotteryTimes() {
            System.out.println("调用了GoldenLottery的addLotteryTimes方法");
            lotteryService.addLotteryTimes();
        }
    }

    还有一个工厂LotteryFactory去获取这两个抽奖类型的bean

    @Component
    public class LotteryFactory {
    
        @Autowired
        private Map<String, ILottery> map;
    
        public ILottery getLotteryFunction(String type){
            return map.get(type);
        }
    }

    为了看是否能注入到Spring的factoryBean,我增加了Service层来测试

    public interface LotteryService {
        void addLotteryTimes();
    }
    @Service
    public class LotteryServiceImpl implements LotteryService {
        @Override
        public void addLotteryTimes() {
            System.out.println("调用了LotteryServiceImpl的addLotteryTimes方法");
        }
    }

    测试代码:

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = DemoApplication.class)
    public class DemoTests {
    
        @Autowired
        private LotteryFactory lotteryFactory;
    
        @Test
        public void factoryTest() {
            ILottery goldenLottery = lotteryFactory.getLotteryFunction(LotteryTypeEnum.GOLDEN.name());
            goldenLottery.addLotteryTimes();
            ILottery singleLottery = lotteryFactory.getLotteryFunction(LotteryTypeEnum.SINGLE.name());
            singleLottery.addLotteryTimes();
        }
    }

    运行结果:

    调用了GoldenLottery的addLotteryTimes方法
    调用了LotteryServiceImpl的addLotteryTimes方法
    调用了SingleLottery的addLotteryTimes方法
    调用了LotteryServiceImpl的addLotteryTimes方法

    说明注入成功.

    总结

    如果没有Spring,我们也可以利用工厂模式配上策略模式达到效果,但是性能和解耦方面可能就没有Spring做的那么好,但是这里学到了如果用Spring容器达到工厂模式的效果.

    展开全文
  • 我们可以用简单工厂来做这事,而且spring依赖注入非常智能! 首先我们定义一个handler接口   public interface Handler { public void print(); } 然后我们实现2个,并且加上spring的注解,注意...

    举一个业务场景:

      假设有一个统一买票旅游的入口,买完票会有一个字段表明种类。

      这些种类表明你去哪,怎么去。

      是坐飞机去奥地利,或是走路到隔壁龙虾店,都不得而知,而且代码逻辑几乎没有关联。这个时候我们难道要这样写代码吗?

      if(type == 1){

            此处洋洋洒洒N多业务方法调用  

        }else if(type == 2){

            此处洋洋洒洒N多业务方法调用  

        }  

    我们可以用简单工厂来做这事,而且spring的依赖注入非常智能!

    首先我们定义一个handler接口

     

    public interface Handler {
        public void print();
    }

    然后我们实现2个,并且加上spring的注解,注意手动给它们取名字哦。

     

    @Component("aHandler")
    public class AHandler implements Handler {
        @Override
        public void print() {
            System.out.println("我是AHandler");
        }
    }
    @Component("bHandler")
    public class BHandler implements Handler {
        @Override
        public void print() {
            System.out.println("我是BHanler");
        }
    }

    我们再写一个工厂去生产它们。

     

    @Component
    public class HandlerFactory {
        @Autowired
        private Map<String , Handler> handlerMap;
    
        public Handler getHandlerByName(String name){
            return handlerMap.get(name);
        }
    }

    好了,一切就绪,现在单元测试跑起来。

     

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = DemoApplication.class)
    public class DemoApplicationTests {
    
        @Autowired
        private HandlerFactory handlerFactory;
    
        @Test
        public void factoryTest() {
            handlerFactory.getHandlerByName("aHandler").print();
            handlerFactory.getHandlerByName("bHandler").print();
        }
    
    }

    于是控制台就打印相应bean下的方法啦~~~

    展开全文
  • Spring依赖注入详解

    千次阅读 2016-07-28 14:03:17
    Spring依赖注入详解 介绍:  所谓依赖注入就是指:在运行期间,有外部容器动态地将依赖对象注入到组件中 分为: 1.setter方法注入 2.使用构造器注入 3.使用Field注入(用于注解方式) 4.使用静态工厂的方法注入...

    Spring依赖注入详解

    介绍:

      所谓依赖注入就是指:在运行期间,有外部容器动态地将依赖对象注入到组件中

    分为:

    1.setter方法注入
    2.使用构造器注入
    3.使用Field注入(用于注解方式)

    4.使用静态工厂的方法注入

    5.普通工厂类的方法注入

    一、Setter方法注入:

    例如1:
    public class Person {
    	private String name;
    	private int age;
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	
    }
    上面是一个纯粹的POJO类 给了 Name 和 Age 两个简单的属性:
    spring.xml中:
    	<bean class="com.test.spring.Person">
    		<property name="name" value="LYW"/>
    		<property name="age" value="21"/>
    	</bean>
    这样配置Bean Person类就被Spring管理了起来
      例如2:
    还有一种复杂的情况:如果Person类 包含了一个Type对象属性,配置就比较繁琐:
    Person.Java
    public class Person {
    	private String name;
    	private int age;
            private Type type;
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	public void setType(Type type) {
    		this.type = type;
    	}
    }
      Type.Java
    public class Type {
    	private String typeName;
    	public void setTypeName(String typeName){
    		this.typeName= typeName;
    	}
    }
    以上这种情况 如何配置?
    有两种方法:1、内置Bean方式:
    	<bean class="com.test.spring.Person">
    		<property name="name" value="LYW"/>
    		<property name="age" value="21"/>
    		<property name="type">
    			<bean class="com.test.spring.Type">
    				<property name="typeName" value="Coding"/>
    			</bean>
    		</property>
    	</bean>
    2、或者采引入属性的方法 :
    spring.xml
    <bean class="com.test.spring.Type" id="type">
    		<property name="typeName" value="Coding"/>
    	</bean>
    
    <bean class="com.test.spring.Person">
    		<property name="name" value="LYW"/>
    		<property name="age" value="21"/>
    		<property name="type" ref="type"> </property>
         </bean>
    

    上下两种配置方式  建议使用下面这种 看起来 方便 不杂乱 

    二、构造器注入: 这种方式的注入是指带有参数的构造函数注入

    例如:
    Person类:
    public class Person {
    	private String name;
    	private int age;
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	public Person(String name, int age) {
    		super();
    		this.name = name;
    		this.age = age;
    	}
    	
    }
         Spring.xml配置:
    <bean id="person" class="com.test.spring.Person
     		<constructor-arg name="name" value="LYW" />
                    <constructor-arg name="age" value="21" />
          </bean>
    这是简单的构造注入
    在配置时 name可以不写 不写会默认按照顺序 注入 
    也根据属性类型注入 <constructor-arg type="int" value="21" /> 这样 不写name 写个type也可以区分

    三、注解注入

    列如:
    UserDao.java
    @Repository
    public class UserDao {
    
        public void sayHello(String name) {
            System.out.println("Hello\t"+name);
        }
    
    }
    UserService.java
    @Service
    public class UserService {
    
        @Autowired
        private UserDao userDao;
    
        public void sayHello(String name) {
            this.userDao.sayHello(name);
        }
    }
    spring.xml
     <context:annotation-config />
        <context:component-scan base-package="com.lyw.*" >
        </context:component-scan>
    这样就可以使用注解的方式注入

    四、静态工厂注入

    使用静态工厂方法创建Bean实例时,class属性也必须指定,但此时class属性并不是指定Bean实例的实现类,而是静态工厂类。因为Spring需要知道是用哪个工厂来创建Bean实例。另外,还需要使用factory-method来指定静态工厂方法名,Spring将调用静态工厂方法(可能包含一组参数),来返回一个Bean实例,一旦获得了指定Bean实例,Spring后面的处理步骤与采用普通方法创建Bean实例则完全一样。需要注意的是,当使用静态工厂方法来创建Bean时,这个factory-method必须要是静态的。
    通俗点讲,就是通过调用静态工厂的方法来获取自己需要的对象,为了让spring管理所有对象,我们不能直接通过"工程类.静态方法()"来获取对象,而是依然通过spring注入的形式获取: 

    public interface Person(){
        public void sayHello();
    }

    下面是接口的两个实现类:

    public class Work implements Person {
        private String msg;
        //依赖注入时必须的setter方法
        public void setMsg(String msg){
            this.msg = msg;
        }
        @Override
        public void sayHello(){
            System.out.println(msg + ",你好");
        }
    }

    public class Teacher implements Person {
        private String msg;
        //依赖注入时必须的setter方法
        public void setMsg(String msg){
            this.msg = msg;
        }
        @Override
        public void sayHello(){
            System.out.println(msg + ",Hello");
        }
    }
    下面的PersonFactory工厂中包含了一个getPerson的静态方法,该方法将根据传入的参数决定创建哪个对象。这是典型的静态工厂设计模式。
    public class PersonFactory {
        public static Animal getPerson(String type){
            if ("work".equalsIgnoreCase(type)){
                return new Work();
            } else {
                return new Teacher();
            }
        }
    }
    spring.xml配置
    <bean id="work" class="com.lyw.spring.PersonFactory" factory-method="getPerson">
        <!-- 配置静态工厂方法的参数 -->
        <constructor-arg value="work" />
        <!-- 通过setter注入的普通属性 -->
        <property name="msg" value="工人" />
    </bean>
    <bean id="teacher" class="com.lyw.spring.PersonFactory" factory-method="getPerson">
        <constructor-arg value="teacher" />
        <property name="msg" value="老师" />
    </bean>
    

    这样就配置完了Spring了

    五、普通工厂类的方法注入

    public class PersonFactory {  
      
        public Person createPerson(){  
            Person person = new Person();  
            person.setName("LYW");  
            person.setAge(22);  
            return person;  
        }  
          
    }  

    spring.xml配置文件 
    <bean id="personFactory" class="com.lyw.spring.PersonFactory"></bean>  
    <bean id="person" factory-bean="personFactory" factory-method="createPerson"></bean>  
    这样配置就可以了 

    以上就是我对Spring注入的总结 希望可以喜欢 


    作者:孩子你狠幼稚,真的很幼稚
    出处:http://blog.csdn.net/lu1005287365/
    如果,您认为阅读这篇博客让您有些收获,不妨点击一下下面的赞【赞一下】
    如果,您希望有什么感觉不对的地方欢迎在下面评论【评一下】
    如果,您对我的博客内容感兴趣,请继续关注我的后续博客,我是【LYW】

    本文版权归作者和CSDN共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    展开全文
  • spring依赖注入原理详解

    千次阅读 2016-08-16 09:23:52
    spring依赖注入原理详解
  • 什么是Spring依赖注入

    千次阅读 2019-11-14 11:53:21
    参考文档 ...什么是Spring依赖注入?...spring依赖注入对调用者和被调用者几乎没有任何要求,完全支持pojo之间依赖关系的管理 new对象:类的头部进行实例化对象和依赖注入一个效果,这个时候该...
  • Spring依赖注入示例

    万次阅读 2020-05-29 14:40:50
    在这个教程中,你将学习什么是 Spring 依赖注入,它是如何工作的,以及如何使用它。 什么是依赖注入依赖注入是您必须了解的Spring基础知识之一。 当您创建一个复杂的应用程序时,很可能会有不同的对象一起工作。 ...
  • Spring依赖注入与自动装配

    万次阅读 2021-02-06 11:13:29
    Spring依赖注入与自动装配 首先推荐狂神说的Spring讲义 1.Beans.xml作用 简而言之,我们通过在beans.xml中进行配置,将各种类交给spring来管理。 2.依赖注入 推荐狂神说Spring03:依赖注入(DI) 这要从控制反转说起...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 203,830
精华内容 81,532
关键字:

对spring依赖注入的实现解释

spring 订阅