java+Spring实现IOC(DI):控制反转(依赖注入)

橙三吉。 提交于 2020-12-15 18:08:01

一、Spring

1.1、概述

Spring是一个Service层框架,可以整合其它许多框架。

Spring的主要技术:

  • IOC(DI):控制反转(依赖注入)
  • AOP:面向切面编程

1.3、IOC(控制反转)

将对象的创建和及对象的生命周期管理过程交给Spring框架来处理,开发过程不再关注对象的的创建和生命周期的管理。

创建过程中Spring可以根据配置对象属性进行设置,这个过程也叫做依赖注入,即DI。

1.3.1、IOC实现原理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="spring_helloworld01" class="main.Spring_helloworld01"></bean>
</beans>
  • **初始化Spring容器:**Spring会解析指定的xml文件,当解析到其中的标签时会根据标签中的class属性指定类的全路径名,通过反射创建改类的对象,并将该对象存入内置的Map中管理。之中键就是id标签,值就是该对象
  • **从容器中获取对象:**根据传入的条件在内置的Map中寻找匹配的键值,如果有则将该键值中保存的对象返回,没有则抛出异常。

推论:

(1)多次获取同一个id的bean,得到的是同一个对象;

(2)同一个类对应不同id,都会在内置Map中有一个键值对,即创建不同对象;

(3)同一个标签不允许配置多个同id的标签。

1.4、Spring创建对象的方法

1.4.1、一般创建(无参构造)

public class Spring_helloworld01 {
   
   
    public void eat(){
   
   
        System.out.println("eat");
    }

    public void say(){
   
   
        System.out.println("say");
    }

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--无参构造-->
        <bean id="spring_helloworld01" class="main.Spring_helloworld01"></bean>
</beans>

实例:

//1、初始化spring容器
ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml");
//2、通过容器获取Bean
Spring_helloworld01 shello=(Spring_helloworld01)context.getBean("spring_helloworld01");

shello.eat();
shello.say();

1.4.2、静态工厂创建

public class Spring_helloworld01 {
   
   
    public  static Calendar getcalendar(){
   
   
        return Calendar.getInstance();
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--静态工厂-->
        <bean id="calender" class="main.Spring_helloworld01" factory-method="getcalendar"></bean>
</beans>

实例:

// 1、初始化spring容器
ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml");

//2、获取bean
Calendar cal=(Calendar)context.getBean("calender");

System.out.println(cal.toString());

1.4.3、实例工厂创建

public class CalendarFactory {
   
   
    public CalendarFactory(String name){
   
   }

    public Calendar getCalendar(){
   
   
        return Calendar.getInstance();
    }
}
public class Spring_helloworld01 {
   
   
    public CalendarFactory getInstance(){
   
   
        return new CalendarFactory("test");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--实例工厂-->
        <bean id="helloInstanceFactory" class="main.Spring_helloworld01" ></bean>
        <bean id="hello1" factory-bean="helloInstanceFactory" factory-method="getInstance"></bean>
</beans>

实例:

// 1、初始化spring容器
ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml");

//2、获取bean
CalendarFactory cal=(CalendarFactory)context.getBean("hello1");

System.out.println(cal.getCalendar());

1.4.4、Spring工厂创建

public class CalendarSpringFactory implements FactoryBean<Calendar> {
   
   

    /**
     * 产生Bean的方法
     * @return
     * @throws Exception
     */
    @Override
    public Calendar getObject() throws Exception {
   
   
        return Calendar.getInstance();
    }

    /**
     * 获取Bean类型的方法
     * @return
     */
    @Override
    public Class<?> getObjectType() {
   
   
        return Calendar.class;
    }

    /**
     * 当前Bean的创建模式:单例或多例
     * @return
     */
    @Override
    public boolean isSingleton() {
   
   
        return true;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--Spring工厂-->
        <bean id="springfac" class="main.CalendarSpringFactory"></bean>
</beans>

实例:

// 1、初始化spring容器
ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml");

//2、获取bean
Calendar cal=(Calendar)context.getBean("springfac");

System.out.println(cal);

1.5、单例和多例

Spring容器管理的Bean默认情况下是单例,即,一个bean创建的对象存在内置map中,无论获取多少次该Bean,返回都是同一个对象。

单例可以减少对象创建,从而减少内存消耗。实际开发中也存在多例的需求,可以将bean设置为多例。

区别

懒加载配置方式: lazy-init

懒加载机制只对单例有用,对于多例没有意义。

<bean id="sp" class="main.SigletonAndPropoty" scope="singleton" lazy-init="true"></bean>

1.6、依赖注入

在对象创建过程中,Spring根据配置对对象属性进行设置,这个过程叫做依赖注入(DI).

方式一: set方法

public class DI {
   
   
    private String name;
    private int age;
    private List<String>  jobs;
    private Set<String> skills;
    private Map<String,String> map;

    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 List<String> getJobs() {
   
   
        return jobs;
    }

    public void setJobs(List<String> jobs) {
   
   
        this.jobs = jobs;
    }

    public Set<String> getSkills() {
   
   
        return skills;
    }

    public void setSkills(Set<String> skills) {
   
   
        this.skills = skills;
    }

    public Map<String, String> getMap() {
   
   
        return map;
    }

    public void setMap(Map<String, String> map) {
   
   
        this.map = map;
    }

    @Override
    public String toString() {
   
   
        return "name:"+name+"\nage:"+age+"\njobs:"+jobs+"\nskills:"+skills+"\nmap:"+map;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--依赖注入-->
        <!--seter方式-->
        <bean id="di" class="main.DI">
                <property name="name" value="jin"></property>
                <property name="age" value="28"></property>
                <property name="jobs">
                        <list>
                             <value>读书</value>
                                <value>写字</value>
                        </list>
                </property>
                <property name="skills">
                        <set>
                                <value>s1</value>
                                <value>s2</value>
                        </set>
                </property>
                <property name="map">
                        <map>
                                <entry key="k1" value="v1"></entry>
                                <entry key="k2" value="v2"></entry>
                        </map>
                </property>
        </bean>

</beans>

实例:

// 1、初始化spring容器
ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml");

//2、获取bean
DI cal=(DI)context.getBean("di");
System.out.println(cal);

自定义类型的注入:

private Dog dog;

class Dog{
   
   
    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;
    }

    @Override
    public String toString() {
   
   
        return "Dog{name:"+name+", age:"+age+"}";
    }
}
<property name="dog" ref="dog"></property>


<bean id="dog" class="main.Dog">
    <property name="name" value="旺财"></property>
    <property name="age" value="5"></property>
</bean>

方式二: 自动装配方法

标签:autowire

  • byName

    Spring会自动根据bean属性的名称找对应id的bean进行属性注入。要求bean的id和bean的属性名称一致。

  • byType

    Spring会根据bean的属性类型找对应class类型的bean属性注入。要求bean的class类型和bean属性类型一致。

    这个类型的bean只有一个,如果有多个会抛异常。

例子:

autowire="byName"
autowire="byType"

方式三:构造方法

public class DI {
   
   
    private String name;
    private int age;
    private List<String>  jobs;
    private Set<String> skills;
    private Map<String,String> map;

    DI(String name,int age,List<String> jobs,Set<String> skills,Map<String,String> map){
   
   
        this.name=name;
        this.age=age;
        this.jobs=jobs;
        this.skills=skills;
        this.map=map;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="di1" class="main.DI">
                <constructor-arg index="0"  type="java.lang.String" value="jin"></constructor-arg>
                <constructor-arg index="1"  type="int" value="28"></constructor-arg>
                <constructor-arg index="2"  >
                        <list>
                                <value>读书</value>
                                <value>写字</value>
                        </list>
                </constructor-arg>
                <constructor-arg index="3" >
                        <set>
                                <value>s1</value>
                                <value>s2</value>
                        </set>
                </constructor-arg>
                <constructor-arg index="4"  >
                        <map>
                                <entry key="k1" value="v1"></entry>
                                <entry key="k2" value="v2"></entry>
                        </map>
                </constructor-arg>
        </bean>
</beans>

实例:

// 1、初始化spring容器
ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml");

//2、获取bean
DI cal=(DI)context.getBean("di1");
System.out.println(cal);

二、Spring注解方式实现IOC和DI

2.1、自定义注解

类似于接口,可以通过@interface声明一个注解。可以通过元注解修饰注解的另一控制自定义注解的特征。

元注解:

  • @target:声明当前定义的注解可以使用的位置。可以在元注解中声明ElementType类型的参数来指定注解使用的位置
  • @Retention:声明当前定义的注解会被保留到什么阶段。可以在RetentionPolicy类型的参数来指定阶段
    • RetentionPolicy.SOURCE:源码阶段,编译过程丢弃
    • RetentionPolicy.CLASS:注解保留在源码和编译阶段,在类加载过程中被删除
    • RetentionPolicy.RUNTIME:注解保留在源码、编译和运行阶段
  • @Documented:指定当前注解是否会被文档提取工具提取到自动生成的文档中。
  • @Inherited:被修饰的注解是否具有继承性,默认没有继承性

在注解中声明属性的过程类似于接口中定义方法。在注解声明中定义的属性需要在使用注解时为属性赋值(在小括号内通过“属性名=属性值”进行,多个属性可以使用逗号隔开)。声明的属性必须为public(默认为public)。声明的属性类型必须为:八大基本类型、String、class、枚举、其它注解、以上类型一维数组类型。

在注解声明中定义属性时可以后跟default关键字指定默认值。

如果注解中只有一个属性值需要被赋值且该属性名叫做value,则value=可以不写。

如果注解的属性为数组类型,且数组只有一个值,则包裹数组的{}可以不写。

2.2、反射注解

/**
* 反射注解
*/
public class NoteDemo {
   
   
    public static void main(String[] args) {
   
   
        Police.fakuan();
        if(Police.class.isAnnotationPresent(Level.class)){
   
   
            Level level=Police.class.getAnnotation(Level.class);
            if("辅警".equals(level.value())){
   
   
                System.out.println("少罚点....");
            }else if("交警".equals(level.value())){
   
   
                System.out.println("罚200....");
            }else if("刑警警".equals(level.value())){
   
   
                System.out.println("罚500....");
            }else{
   
   
                System.out.println("交钱....");
            }
        }else{
   
   
            System.out.println("假警察....");
        }

    }
}

/**
 * 自定义注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Level{
   
   
    String value() default "交警";
}

@Level("辅警")
class Police{
   
   
    public static void fakuan(){
   
   
        System.out.println("罚款....");
    }
}

2.3、Spring注解IOC

这种方式效率高,配置清晰、修改方便,推荐使用。所谓注解,就是给程序看的提示信息,很多时候作为轻量级的配置方式。

  1. 开启包扫描
<context:component-scan base-package="main"></context:component-scan>
  1. 使用注解注册bean

在配置的包中的类上使用@Component注解,这个类自动被注册为bean,使用当前类的class的为的class,通常情况下使用类名首字母的小写为id。

例子:

@Component
public class Person {
   
   
}

**bean的id:**通常情况下注解注册的bean使用类名首字母小写为bean的id,但是如果类名第二个字母为大写则首字母保留原样。

也可以通过@Component的value指定id

2.4、Spring注解DI

  1. 开启注解方式注入
<context:annotation-config></context:annotation-config>
  1. 使用注解依赖注入(DI)
@Component("pson")
public class Person {
   
   
    //普通变量的注入
    @Value("jin")
    private String name;
    @Value("15")
    private int age;
    
    //集合类型的依赖注入
    @Value("#{@l1}")
    private List<String> jobs;
    @Value("#{@s1}")
    private Set<String> skills;
    @Value("#{@m1}")
    private Map<String,String> map;

    @Override
    public String toString() {
   
   
        return "name:"+name+"\nage:"+age+"\njobs:"+jobs+"\nskills:"+skills+"\nmap:"+map;
    }
}

注意:可以通过设置@Component中的Value值自定义bean的id值

集合类型的依赖注入配置:

<!--配置集合数据-->
<util:list id="l1">
    <value>play</value>
    <value>eat</value>
</util:list>

<util:set id="s1">
    <value>compute</value>
    <value>iphon</value>
</util:set>

<util:map id="m1">
    <entry key="k1" value="v1"></entry>
    <entry key="k2" value="v2"></entry>
</util:map>

补充:外部配置文件依赖注入

  1. 加载外部属性配置文件
<context:property-placeholder location="classpath:/db.properties"></context:property-placeholder>
  1. 使用注释依赖注入
@Component
public class MysqlConnector {
   
   
    @Value("${dirverName}")
    private String dirverName;
    @Value("${url}")
    private String url;
    @Value("${name}")
    private String name;
    @Value("${password}")
    private String password;

    @Override
    public String toString() {
   
   
        return "dirverName:"+dirverName+"\nurl:"+url+"\nname:"+name+"\npassword:"+password;
    }
}

db.properties文件:

dirverName=com.sql.jbdc.Driver
url=jdbc:mysql://SpringDB
name=root
password=root

2.5、其他注解

  • **@Scope:**单例/多例设置

    • ConfigurableBeanFactory.SCOPE_PROTOTYPE:多例
    • ConfigurableBeanFactory.SCOPE_SINGLETON:单例

    默认情况下为单例。

    @Lazy:设置单例为懒加载

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)//多例
@Lazy
public class OtherPerson {
   
   
    public void eat(){
   
   
        System.out.println("吃。。。");
    }
}
  • @PostConstruct和 @PreDestroy

@PostConstruct:初始化方法

@PreDestroy::销毁方法

@Component
public class OtherPerson {
   
   
    public void eat(){
   
   
        System.out.println("吃。。。");
    }

    @PostConstruct
    public  void Init(){
   
   
        System.out.println("初始化。。。");
    }

    @PreDestroy
    public void Destroy(){
   
   
        System.out.println("销毁。。。");
    }
}
  • @Controller、@Service、@Repository和@Component:修饰类声明为Spring管理的bean

@Controller:web层

@Service:服务器层

@Repository:数据访问层

@Component::通用

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!