Spring笔记

1、Spring

1.1、简介

  • Spring:春天----> 给软件行业带来了春天

  • 2002,首次推出了Spring框架的出行:interface21框架

  • Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日正式发布1.0版本

  • Rod Johnson ,Spring Framework创始人,著名作者,很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他不是计算机专业的,而是音乐学。

  • Spring理念:是现有的技术更加容易使用,本身是一个大杂烩。

  • SSH:Struct2 + Spring + Hibernate

  • SSM: SpringMVC + Spring + Mybatis

官网:Spring Framework

官方下载地址:JFrog (spring.io)

github地址:spring-projects/spring-framework: Spring Framework (github.com)

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.19</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.19</version>
</dependency>

1.2、优点

  • spring是开源的免费的容器。
  • spring是一个轻量级的,非入侵式的框架。
  • 控制反转(IOC),面向切面编程 (AOP)。
  • 支持事务处理,对框架整合的支持。

总结:spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架!

1.3、组成

20210713090020684.png (1106×677) (csdnimg.cn)

2、IoC理论推导

  1. UserDao 接口
  2. UserDaoImpl 实现类
  3. UserService 业务接口
  4. UserServiceImpl 业务实现类

在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改源代码!如果代码量十分大,修改一次的成本十分昂贵!

1652332006327

我们使用一个set接口实现动态注入,已经发生了革命性变化

//利用set实现值的动态注入
public void setUserDao(UserDao userDao) {
    this.userDao = userDao;
}
  • 之前,程序是主动创建对象,控制权在程序员手上!
  • 使用了set注入后,程序不再具有主动性,而是变成了被动的接收对象

这种思想,从本质上解决了问题,程序员不用再去管理对象的创建,系统的耦合性大大降低~ 可以更加专注在业务的实现上!这时IOC的原型!

1652332019817

IOC本质

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。

51139210101_5.png (627×565) (h5w3.com)

采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。

3、HelloSpring

  1. 导入jar包

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.19</version>
    </dependency>
    
  2. 编写Hello实体类

    public class Hello {
        private String str;
    
        public String getStr() {
            return str;
        }
    
        public void setStr(String str) {
            this.str = str;
        }
    
        @Override
        public String toString() {
            return "Hello{" +
                    "str='" + str + '\'' +
                    '}';
        }
    }
    
  3. 编写beans.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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--使用Spring 来创建对象,在Spring中这些都称为Bean
    
        类型 变量名 = new 类型();
        Hello hello = new Hello();
    
        id = 变量名
        class = new 对象
        property 相当于给对象中的属性设置一个值
        -->
        <bean id="hello" class="com.kong.pojo.Hello">
            <property name="str" value="Spring"/>
        </bean>
        
    </beans>
    
  4. 测试

    public class MyTest {
        public static void main(String[] args) {
            // 获取Spring的上下文对象
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            // 我们的对象都在Spring中管理  我们要使用,直接去里面取出来就可以!
            Hello hello = (Hello) context.getBean("hello");
            System.out.println(hello.toString());
        }
    }
    

思考:

  • Hello 对象是谁创建的 ? 【hello 对象是由Spring创建的】
  • Hello 对象的属性是怎么设置的 ? 【hello 对象的属性是由Spring容器设置的】

这个过程就叫控制反转 :

  • 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的

  • 反转 : 程序本身不创建对象 , 而变成被动的接收对象 .

  • 依赖注入 : 就是利用set方法来进行注入的.

IOC是一种编程思想,由主动的编程变成被动的接收 , 要实现不同的操作 , 只需要在xml配置文件中进行修改 , 所谓的IoC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !

4、IoC创建对象的方式

  1. 使用无参构造创建对象,默认!

    <bean id="user" class="com.kong.pojo.User">
        <property name="name" value="kong"/>
    </bean>
    
  2. 假设我们要使用有参构造创建对象

    1. 下标赋值

      <!--第一种,下标赋值-->
      <bean id="user" class="com.kong.pojo.User">
          <constructor-arg index="0" value="kong"/>
      </bean>
      
    2. 参数类型赋值,不建议使用(若多个参数类型相同,按照参数顺序赋值)

      <bean id="user" class="com.kong.pojo.User">
          <constructor-arg type="java.lang.String" value="kong"/>
      </bean>
      
    3. 第三种方式,通过参数名

      <!--第三种方式,通过参数名-->
      <bean id="user" class="com.kong.pojo.User">
          <constructor-arg name="name" value="kong"/>
      </bean>
      

总结:spring在加载配置文件beans.xml 的时候,容器中管理的对象就已经初始化了!

5、Spring 配置

5.1、别名

<!--别名-->
<alias name="user" alias="u1"/>

5.2、Bean的配置

<!--
    id:bean 的唯一标识符,也就是相当于对象名
    class:bean 对象所对应的全限定名:包名+类名
    name:也就是别名,而且name 可以同时取多个别名
-->
<bean id="userT" class="com.kong.pojo.UserT" name="user2 u2,u3;u4">
    <constructor-arg name="name" value="kong"/>
</bean>

5.3、import

这个import,一般用于团队开发使用,它可以将多个配置文件,导入合并为一个

假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用 import 将所有人的beans.xml合并为一个总的!

  • 张三

  • 李四

  • 王五

  • applicationContext.xml

    <import resource="beans.xml"/>
    <import resource="beans2.xml"/>
    <import resource="beans3.xml"/>
    

使用的时候,直接使用总的配置就可以了

6、依赖注入(DI)

6.1、构造器注入

第4章已经说过

<constructor-arg></constructor-arg>

6.2、set方式注入【重点】

set方法注入主要是property

<property></property>
  • 依赖注入:Set注入
    • 依赖:bean对象的创建依赖于容器!
    • 注入:bean对象的所有属性由容器注入!

环境搭建:

  1. 复杂类型

    public class Address {
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    }
    
  2. 真实测试对象

    public class Student {
        private String name;
        private Address address;
        private String[] books;
        private List<String> hobbies;
        private Map<String,String> cards;
        private Set<String> games;
        private String wife;
        private Properties info;
    }
    
  3. beans.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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="student" class="com.kong.pojo.Student">
            <!--第一种,普通值注入,value-->
            <property name="name" value="kong"/>
        </bean>
    
    </beans>
    
  4. 测试类

    public class MyTest {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            Student student = (Student) context.getBean("student");
            System.out.println(student.getName());
        }
    }
    

完善注入信息:

<bean id="address" class="com.kong.pojo.Address">
    <property name="address" value="China"/>
</bean>
<bean id="student" class="com.kong.pojo.Student">
    <!--第一种,普通值注入,value-->
    <property name="name" value="kong"/>
    <!--第二种,bean注入,ref-->
    <property name="address" ref="address"/>
    <!--第三种,数组注入-->
    <property name="books">
        <array>
            <value>红楼梦</value>
            <value>西游记</value>
            <value>水浒传</value>
            <value>三国演义</value>
        </array>
    </property>

    <!--第四种,list注入-->
    <property name="hobbies">
        <list>
            <value>听歌</value>
            <value>敲代码</value>
            <value>看电影</value>
        </list>
    </property>

    <!--Map注入-->
    <property name="cards">
        <map>
            <entry key="身份证" value="123456789"/>
            <entry key="银行卡" value="111111"/>
        </map>
    </property>

    <!--set-->
    <property name="games">
        <set>
            <value>LOL</value>
            <value>COC</value>
            <value>BOB</value>
        </set>
    </property>

    <!--null-->
    <property name="wife">
        <null/>
    </property>

    <!--Properties-->
    <property name="info">
        <props>
            <prop key="driver">2022</prop>
            <prop key="url"></prop>
            <prop key="username">root</prop>
            <prop key="password">123456</prop>
        </props>
    </property>
</bean>

6.3、拓展方式注入

我们可以使用P命名空间和C命名空间进行注入

官方解释:

1652347765717

P命名空间注入 (Set属性注入)

beans.xml 文件头部导入P命名空间约束文件

xmlns:p="http://www.springframework.org/schema/p"

编写xml

<!--p命名空间注入,可以直接注入属性的值-->
<bean id="user" class="com.kong.pojo.User" p:name="kong" p:age="18"/>

测试

@Test
public void test() {
    ApplicationContext context = new ClassPathXmlApplicationContext("userBeans.xml");
    User user = context.getBean("user", User.class);
    System.out.println(user);
}

C命名空间(构造器注入)

导入C命名空间约束文件

xmlns:c="http://www.springframework.org/schema/c"

编写xml

<!--c命名空间,通过构造器注入:constructor-args-->
<bean id="user2" class="com.kong.pojo.User" c:name="kong" c:age="19"/>

测试

@Test
public void test() {
    ApplicationContext context = new ClassPathXmlApplicationContext("userBeans.xml");
    User user = context.getBean("user2", User.class);
    System.out.println(user);
}

注意点:p命名空间和c命名空间不能直接使用,需要导入xml约束文件

xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

6.4、Bean的作用域

1652347993495

  1. 单例模式(Spring默认模式)

    <bean id="user2" class="com.kong.pojo.User" c:name="kong" c:age="19" scope="singleton"/>
    
  2. 原型模式:每次从容器中get的时候都会产生一个新对象

    <bean id="user2" class="com.kong.pojo.User" c:name="kong" c:age="19" scope="prototype"/>
    
  3. 其余的request、session、application、websocket,这些只能在web开发中使用到!

7、Bean的自动装配

  • 自动装配是Spring满足bean依赖的一种方式!【该bean的属性依赖另一个bean】
  • Spring会在上下文中自动寻找,并自动给bean装配属性!
  • 不能自动装配简单数据类型,比如 int、boolean、String 等。

在Spring中有三种装配的方式:

  1. 在xml中显式的配置
  2. 在java中显式配置
  3. 隐式的自动装配 【重要!】

7.1、测试

环境搭建:一个人有两个宠物

例子相同:spring——自动装配【非常详细】 - 百度文库 (baidu.com)

7.2、ByName自动装配

byName:会自动在容器上下文中查找,和自己对象set方法名后面的值对应的bean id

注意:

  • set后面的首字母会自动变成小写,bean id首字母大写时,取不到
  • set方法名后面的值 必须和 bean id 名对应!
<bean id="cat" class="com.kong.pojo.Cat"></bean>
<bean id="dog" class="com.kong.pojo.Dog"></bean>
<!--
byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id
-->
<bean id="people" class="com.kong.pojo.People" autowire="byName">
    <property name="name" value="kong"/>
</bean>

7.3、ByType自动装配

byType:会自动在容器上下文中查找,和自己对象的该属性类型相同的bean!

注意:

  • 对象属性不能有相同类型!保证class唯一

  • 依赖 bean 的 id 可以省略【因为根据class来查找】

<bean class="com.kong.pojo.Cat"></bean>
<bean class="com.kong.pojo.Dog"></bean>
<!--
byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean!
-->
<bean id="people" class="com.kong.pojo.People" autowire="byType">
    <property name="name" value="kong"/>
</bean>

小结:

  • byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致!
  • byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!

1652425841259

7.4、使用注解实现自动装配

jdk1.5支持的注解,Spring2.5就支持注解了

要使用注解须知:

  1. 导入context约束

    xmlns:context="http://www.springframework.org/schema/context"
    
  2. 配置注解的支持

    <!--开启注解支持-->
    <context:annotation-config/>
    
    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:annotation-config/>
    
    </beans>
    

@Autowired

  • 直接在属性上使用即可!也可以在set方法上使用

  • 使用Autowired,我们可以不用编写set方法了,前提是这个自动装配的属性在IOC容器中仅存在一个该类型(即 默认是 byType)。

  • 如果有多个类型一样的Bean候选者,Spring会自动按照名称(byName )进行装配,若无法匹配上,则需要配合@Qualifier,指定一个唯一的bean对象。

    指定名称后,如果Spring IOC容器中没有对应的组件bean抛出NoSuchBeanDefinitionException。也可以将@Autowired中required配置为false(默认为true,看下面源码),如果配置为false之后,当没有找到相应bean的时候,系统不会抛异常

    public @interface Autowired {
        boolean required() default true;
    }
    
public class People {
    @Autowired
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;
}
public class People {
    @Autowired
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog111")
    private Dog dog;
    private String name;
}

@Resource

在javax.annotation包下

默认 byName

public class People {
    @Resource(name = "cat111")
    private Cat cat;
    @Resource
    private Dog dog;
    private String name;
}

小结:

@Resource 和 @Autowired 的区别:

  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired 默认通过 byType 方式实现,如果使用了@Qualifier,则直接byName,不会再byType 【常用】
  • @Resource 默认通过byName方式实现,如果找不到名字,则通过byType 方式实现,如果两个都找不到,则报异常!

@Inject

需要导入javax.inject.Inject jar包

根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named;

@Inject
@Named("BMW")
private Car car;

小结:

  • @Autowired是Spring自带的,@Resource是JSR250规范实现的,@Inject是JSR330规范实现的

  • @Inject用法与@Autowired基本一样,不同的是@Inject没有required属性

  • @Autowired、@Inject是默认按照类型(byType)匹配的,@Resource是按照名称(byName)匹配的

  • @Autowired如果需要按照名称匹配需要和@Qualifier一起使用,@Inject和@Named一起使用,@Resource则通过name进行指定

8、使用注解开发

在Spring4之后,要使用注解开发,必须要保证aop的包导入了

1652430460207

使用注解,需要导入context约束,增加注解的支持

<?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.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启注解支持-->
    <context:annotation-config/>

    


</beans>
  1. bean

    @Component 组件放在类上,说明这个类被Spring管理了,就是bean!

  2. 属性如何注入

    @Component
    public class User {
    
        public String name;
        
        //相当于 <property name="name" value="kong"/>
        @Value("kong")
        public void setName(String name) {
            this.name = name;
        }
    }
    
  3. 衍生的注解

    @Component 有几个衍生注解,我们在web开发中,会按照mvc三层架构分层

    • dao 【@Repository】
    • Service 【@Service】
    • controller 【@Controller】

    这四个注解功能一样,都是讲某个类注册到Spring容器中装配!

  4. 自动装配

    @Autowired
    @Resource
    
  5. 作用域

    @Component
    @Scope("singleton")
    public class User {
    
        public String name;
    
        //相当于 <property name="name" value="kong"/>
        @Value("kong")
        public void setName(String name) {
            this.name = name;
        }
    }
    
  6. 小结

    xml 与 注解:

    • xml更加万能,适用于任何场合!维护更加方便
    • 注解 不是自己类使用不了,维护相对复杂!

    xml 与 注解 最佳实践:

    • xml用来管理bean;

    • 注解只负责完成属性的注入;

    • 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持

      <!--开启注解支持-->
      <context:component-scan base-package="com.kong"/>
      <context:annotation-config/>
      

9、使用Java的方式配置Spring

JavaConfig 是Spring的一个子项目,在Spring4之后,它成为了一个核心功能

1652433432515

配置类

@Configuration //这个也会被Spring注册到容器中,因为它本来就是一个@Component
//@Configuration 代表这时一个配置类,就和我们之前beans.xml一样
@Import(KongConfig2.class)

public class KongConfig {

    // 注册一个bean,就相当于我们之前写的一个bean标签
    // 这个方法的名字,就相当于bean标签的id属性
    // 这个方法的返回值,就相当于bean标签的class属性
    @Bean
    public User getUser(){
        return new User(); //返回要注入Spring的对象
    }
}

实体类

public class User {
    private String name;

    public String getName() {
        return name;
    }

    @Value("kong") //属性注入值
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

测试类

public static void main(String[] args) {
    //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig 上下文来获取容器,通过配置类的class对象加载!
    ApplicationContext context = new AnnotationConfigApplicationContext(KongConfig.class);
    User getUser = (User) context.getBean("getUser");

    System.out.println(getUser);
}

10、代理模式

为什么要学习代理模式?因为这就是Spring AOP的底层!【Spring AOP 和 SpringMVC】

代理模式的分类:

  • 静态代理
  • 动态代理

1653032346094

10.1、静态代理

本质:代理类中包含被代理类的引用。

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色
  • 客户:访问代理对象的人

代码步骤:

  1. 接口

    //租房
    public interface Rent {
        public void rent();
    }
    
  2. 真实角色

    //房东
    public class Host implements Rent{
    
        @Override
        public void rent() {
            System.out.println("房东要出租房子!");
        }
    }
    
  3. 代理角色

    public class Proxy implements Rent{
    
        private Host host;
    
        public Proxy() {
        }
        public Proxy(Host host) {
            this.host = host;
        }
    
        @Override
        public void rent() {
            seeHouse();
            host.rent();
            sign();
            fee();
        }
    
        //看房
        public void seeHouse() {
            System.out.println("中介带你去看房");
        }
    
        //收费
        public void fee() {
            System.out.println("收中介费");
        }
    
        //签合同
        public void sign() {
            System.out.println("签合同");
        }
    }
    
  4. 客户端访问代理角色

    public class Client {
        public static void main(String[] args) {
            //房东要租房子
            Host host = new Host();
            //代理,中介帮房东租房子,代理角色一般会有附属操作
            Proxy proxy = new Proxy(host);
            //你去找中介租房
            proxy.rent();
        }
    }
    

代理模式的好处:

  • 可以使真实觉得的操作更加纯粹,不用去关注一些公共业务
  • 公共业务就交给代理角色,实现了业务的分工
  • 公共业务发生扩展的时候,方便集中管理

缺点:

  • 一个真实角色就会产生一个代理角色,代码量翻倍,开发效率低

10.2、加深理解

聊聊AOP

1652508865545

10.3、动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口:JDK 动态代理【我们在这里使用】
    • 基于类:cglib
    • java字节码实现:javasist

使用基于接口的动态代理,需要了解两个类:

  • Proxy:代理
  • InvocationHandler:调用处理程序

Proxy 是Java 的一个类

1653034154287

1653034280685

InvocationHandler 是 Java的一个接口,里面只有一个invoke() 方法

1653033922616

1653034041862

1.动态生成代理类

Proxy.newProxyInstance(代理对象类加载器,代理接口类,InvocationHandler)

2.调用方法

自动调用InvocationHandler 类的method.invoke(代理接口对象,args)方法执行

//租房接口
public interface Rent {
    public void rent();
}

//目标类:房东
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东要出租房子!");
    }
}

//用这个类动态生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Rent rent;
    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成得到代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(rent.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }

    //处理代理实例 并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质就是使用反射机制
        Object result = method.invoke(rent, args);
        return result;
    }
}

//客户端
public class Client {
    public static void main(String[] args) {
        //目标类
        Host host = new Host();
        //代理类,现在没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象
        pih.setRent(host);
        //动态生成的代理类
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }
}

11、AOP

11.1、什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

640 (952×551) (qpic.cn)

11.2、AOP在Spring中的作用

提供声明式事务;允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 ....
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。在AOP中表示为在哪干和干什么集合
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。在AOP中表示为干什么
  • 目标(Target):被通知对象。在AOP中表示为对谁干
  • 代理(Proxy):向目标对象应用通知之后创建的对象。在AOP中表示为怎么实现的一种典型方式
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。在AOP中表示为在哪里干的集合
  • 连接点(JointPoint):与切入点匹配的执行点。在AOP中表示为在哪里干

640 (744×471) (qpic.cn)

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

前置通知(Before advice):在某连接点之前执行的通知,但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。

后置通知(After returning advice):在某连接点正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。

异常通知(After throwing advice):在方法抛出异常退出时执行的通知。

最终通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

环绕通知(Around Advice):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。

即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

11.3、Spring AOP 和 AspectJ关系

Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。

Spring AOP 是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。

ApectJ采用的就是静态织入的方式,ApectJ主要采用的是编译期织入。

11.4、使用Spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包!

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.9.1</version>
</dependency>

方式一:使用Spring的API接口 【主要是SpringAPI接口实现】

<!--注册bean-->
<bean id="userService" class="com.kong.service.UserServiceImpl"/>
<bean id="log" class="com.kong.log.Log"/>
<bean id="afterLog" class="com.kong.log.AfterLog"/>

<!--方式一:使用原生Spring API接口-->
<!--配置aop:需要导入aop的约束-->
<aop:config>
    <!--切入点 expression:表达式,execution(要执行的位置)-->
    <aop:pointcut id="pointcut" expression="execution(* com.kong.service.UserServiceImpl.*(..))"/>

    <!--执行环绕-->
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>

方式二:自定义类来实现AOP 【主要是切面定义】

<!--方式二:自定义类-->
<bean id="diy" class="com.kong.diy.DiyPointCut"/>
<aop:config>
    <!--自定义切面,ref要引用的类-->
    <aop:aspect ref="diy">
        <!--切入点-->
        <aop:pointcut id="point" expression="execution(* com.kong.service.UserServiceImpl.*(..))"/>
        <!--通知-->
        <aop:before method="before" pointcut-ref="point"/>
        <aop:after method="after" pointcut-ref="point"/>
    </aop:aspect>
</aop:config>

方式三:使用注解实现

<!--方式三-->
<bean id="annotationPointCut" class="com.kong.diy.AnnotationPointCut"/>
<!--开启注解支持   JDK(默认 proxy-target-class="false")  cglib(proxy-target-class="true")-->
<aop:aspectj-autoproxy proxy-target-class="false"/>
@Aspect //标注这个类为切面
public class AnnotationPointCut {

    @Before("execution(* com.kong.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("=======方法执行前========");
    }

    @After("execution(* com.kong.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("=======方法执行后========");
    }

    //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.kong.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("=======环绕前========");
        
        //执行方法
        Object proceed = joinPoint.proceed();

        System.out.println("=======环绕后========");
    }
}

12、整合Mybatis

步骤:

  1. 导入相关jar包
    • junit
    • mybatis
    • mysql数据库
    • spring相关的
    • aop织入
    • mybatis-spring 【new】
  2. 编写配置文件
  3. 测试

12.1、回忆mybatis

  1. 编写实体类

    package com.kuang.pojo;
    
    public class User {
       private int id;  //id
       private String name;   //姓名
       private String pwd;   //密码
    }
    
  2. 编写mybatis核心配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
           PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
           "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
       <typeAliases>
           <package name="com.kuang.pojo"/>
       </typeAliases>
    
       <environments default="development">
           <environment id="development">
               <transactionManager type="JDBC"/>
               <dataSource type="POOLED">
                   <property name="driver" value="com.mysql.jdbc.Driver"/>
                   <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
                   <property name="username" value="root"/>
                   <property name="password" value="123456"/>
               </dataSource>
           </environment>
       </environments>
    
       <mappers>
           <package name="com.kuang.dao"/>
       </mappers>
    </configuration>
    
  3. 编写UserDao接口

    public interface UserMapper {
       public List<User> selectUser();
    }
    
  4. 编写对应UserMapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
           PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
           "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.kuang.dao.UserMapper">
    
       <select id="selectUser" resultType="User">
        select * from user
       </select>
    
    </mapper>
    
  5. 测试

    @Test
    public void selectUser() throws IOException {
    
       String resource = "mybatis-config.xml";
       InputStream inputStream = Resources.getResourceAsStream(resource);
       SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
       SqlSession sqlSession = sqlSessionFactory.openSession();
    
       UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
       List<User> userList = mapper.selectUser();
       for (User user: userList){
           System.out.println(user);
      }
    
       sqlSession.close();
    }
    

12.2、Mybatis-Spring

引入Spring之前需要了解mybatis-spring包中的一些重要类;

http://www.mybatis.org/spring/zh/index.html

图片

什么是 MyBatis-Spring?

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。

知识基础

在开始使用 MyBatis-Spring 之前,你需要先熟悉 Spring 和 MyBatis 这两个框架和有关它们的术语。这很重要

MyBatis-Spring 需要以下版本:

MyBatis-Spring MyBatis Spring 框架 Spring Batch Java
2.0 3.5+ 5.0+ 4.0+ Java 8+
1.3 3.4+ 3.2.2+ 2.1+ Java 6+

如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:

<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis-spring</artifactId>
   <version>2.0.2</version>
</dependency>

要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:

一个 SqlSessionFactory 和至少一个数据映射器类

在 MyBatis-Spring 中,可使用SqlSessionFactoryBean来创建 SqlSessionFactory。要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
 <property name="dataSource" ref="dataSource" />
</bean>

注意:SqlSessionFactory需要一个 DataSource(数据源)。这可以是任意的 DataSource,只需要和配置其它 Spring 数据库连接一样配置它就可以了。

在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。

在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。

SqlSessionFactory有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。

一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 < settings> 或 < typeAliases>元素。

需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(<environments>),数据源(<DataSource>)和 MyBatis 的事务管理器(<transactionManager>)都会被忽略。SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值。

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。

模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。

可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
	<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:

public class UserDaoImpl implements UserDao {

	private SqlSession sqlSession;

	public void setSqlSession(SqlSession sqlSession) {
       this.sqlSession = sqlSession;
    }

    public User getUser(String userId) {
        return sqlSession.getMapper...;
    }
}

按下面这样,注入 SqlSessionTemplate:

<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
	<property name="sqlSession" ref="sqlSession" />
</bean>

整合实现一

1、引入Spring配置文件beans.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"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    
</beans>

2、配置数据源替换mybaits的数据源

<!--配置数据源:数据源有非常多,可以使用第三方的,也可使使用Spring的-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
   <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
   <property name="username" value="root"/>
   <property name="password" value="123456"/>
</bean>

3、配置SqlSessionFactory,关联MyBatis

<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
   <property name="dataSource" ref="dataSource"/>
   <!--关联Mybatis-->
   <property name="configLocation" value="classpath:mybatis-config.xml"/>
   <property name="mapperLocations" value="classpath:com/kuang/dao/*.xml"/>
</bean>

4、注册sqlSessionTemplate,关联sqlSessionFactory;

<!--注册sqlSessionTemplate , 关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
   <!--利用构造器注入-->
   <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

5、增加Dao接口的实现类;私有化sqlSessionTemplate

public class UserDaoImpl implements UserMapper {

   //sqlSession不用我们自己创建了,Spring来管理
   private SqlSessionTemplate sqlSession;

   public void setSqlSession(SqlSessionTemplate sqlSession) {
       this.sqlSession = sqlSession;
  }

   public List<User> selectUser() {
       UserMapper mapper = sqlSession.getMapper(UserMapper.class);
       return mapper.selectUser();
  }
   
}

6、注册bean实现

<bean id="userDao" class="com.kuang.dao.UserDaoImpl">
   <property name="sqlSession" ref="sqlSession"/>
</bean>

7、测试

@Test
public void test2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    UserMapper mapper = (UserMapper) context.getBean("userDao");
    List<User> user = mapper.selectUser();
    System.out.println(user);
}

结果成功输出!现在我们的Mybatis配置文件的状态!发现都可以被Spring整合!

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
       PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
       "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
   <typeAliases>
       <package name="com.kuang.pojo"/>
   </typeAliases>
    
</configuration>

整合实现二

mybatis-spring1.2.3版以上的才有这个 .

官方文档截图 :

dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起方式1 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看

图片

测试:

1、将我们上面写的UserDaoImpl修改一下

public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {
   public List<User> selectUser() {
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.selectUser();
  }
}

2、修改bean的配置

<bean id="userDao" class="com.kuang.dao.UserDaoImpl">
   <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

3、测试

@Test
public void test2(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   UserMapper mapper = (UserMapper) context.getBean("userDao");
   List<User> user = mapper.selectUser();
   System.out.println(user);
}

13、声明式事务

13.1、回顾事务

  • 一组业务要么都成功,要么都失败
  • 事务在项目开发中,十分重要,涉及到数据的一致性问题,不能马虎!
  • 确保完整性和一致性

事务的ACID原则:

  • 原子性:要么都成功,要么都失败
  • 一致性:事务操作前后,资源和状态保持一致,防止脏读和幻读等
  • 隔离性:多个事务操作互相隔离,互不影响
  • 持久性:事务一旦提交,就持久化写入存储器中

13.2、spring中的事务管理

测试

将上面的代码拷贝到一个新项目中

在之前的案例中,我们给userDao接口新增两个方法,删除和增加用户;

//添加一个用户
int addUser(User user);

//根据id删除用户
int deleteUser(int id);

mapper文件,我们故意把 deletes 写错,测试!

<insert id="addUser" parameterType="com.kuang.pojo.User">
    insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>

<delete id="deleteUser" parameterType="int">
    deletes from user where id = #{id}
</delete>

编写接口的实现类,在实现类中,我们去操作一波

public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {

   //增加一些操作
   public List<User> selectUser() {
       User user = new User(4,"小明","123456");
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       mapper.addUser(user);
       mapper.deleteUser(4);
       return mapper.selectUser();
  }

   //新增
   public int addUser(User user) {
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.addUser(user);
  }
   //删除
   public int deleteUser(int id) {
       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
       return mapper.deleteUser(id);
  }

}

测试

@Test
public void test2(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   UserMapper mapper = (UserMapper) context.getBean("userDao");
   List<User> user = mapper.selectUser();
   System.out.println(user);
}

报错:sql异常,delete写错了

结果 :插入成功!

没有进行事务的管理;我们想让他们都成功才成功,有一个失败,就都失败,我们就应该需要事务!

以前我们都需要自己手动管理事务,十分麻烦!

但是Spring给我们提供了事务管理,我们只需要配置即可;

Spring中的事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。

编程式事务管理

  • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
  • 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

声明式事务管理

  • 一般情况下比编程式事务好用。
  • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
  • 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

使用Spring管理事务,注意头文件的约束导入 : tx

xmlns:tx="http://www.springframework.org/schema/tx"

http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

事务管理器

  • 无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。
  • 就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法。

JDBC事务

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
</bean>

配置好事务管理器后我们需要去配置事务的通知

<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
       <!--配置哪些方法使用什么样的事务,配置事务的传播特性-->
       <tx:method name="add" propagation="REQUIRED"/>
       <tx:method name="delete" propagation="REQUIRED"/>
       <tx:method name="update" propagation="REQUIRED"/>
       <tx:method name="search*" propagation="REQUIRED"/>
       <tx:method name="get" read-only="true"/>
       <tx:method name="*" propagation="REQUIRED"/>
   </tx:attributes>
</tx:advice>

spring事务传播特性:

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。

就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!

配置AOP

导入aop的头文件!

<!--配置aop织入事务-->
<aop:config>
   <aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>
   <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

进行测试

删掉刚才插入的数据,再次测试!

@Test
public void test2(){
   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
   UserMapper mapper = (UserMapper) context.getBean("userDao");
   List<User> user = mapper.selectUser();
   System.out.println(user);
}

思考问题?

为什么需要配置事务?

  • 如果不配置,就需要我们手动提交控制事务;
  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!

补:IoC

如何理解IoC

Spring Bean 是什么

bean就类似是定义的一个组件,这个组件的作用是实现某个功能。简单来说,Bean 代指的就是那些被 IoC 容器所管理的对象。

IoC是什么

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

IoC能做什么

传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活

IoC与DI的关系

控制反转是通过依赖注入实现的,其实它们是同一个概念的不同角度描述。通俗来说就是IoC是设计思想,DI是实现方式

DI—Dependency Injection,即依赖注入:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

IoC配置的三种方式

xml配置

顾名思义,就是将bean的信息配置.xml文件里,通过Spring加载文件为我们创建bean。

举例:

  1. 配置xx.xml文件
  2. 声明命名空间和配置bean
<?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">
    <!-- services -->
    <bean id="userService" class="tech.pdai.springframework.service.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions for services go here -->
</beans>

Java 配置

将类的创建交给我们配置的JavcConfig类来完成,Spring只负责维护和管理,采用纯Java创建方式。

举例:

  1. 创建一个配置类, 添加@Configuration注解声明为配置类

  2. 创建方法,方法上加上@bean,该方法用于创建实例并返回,该实例创建后会交给spring管理,方法名建议与实例名相同(首字母小写)。注:实例类不需要加任何注解

@Configuration
public class BeansConfig {

    /**
     * @return user dao
     */
    @Bean("userDao")
    public UserDaoImpl userDao() {
        return new UserDaoImpl();
    }

    /**
     * @return user service
     */
    @Bean("userService")
    public UserServiceImpl userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao());
        return userService;
    }
}

注解配置

通过在类上加注解的方式,来声明一个类交给Spring管理,Spring会自动扫描带有@Component,@Controller,@Service,@Repository这四个注解的类,然后帮我们创建并管理,前提是需要先配置Spring的注解扫描器。

  1. 对类添加@Component相关的注解,比如@Controller,@Service,@Repository

  2. 设置ComponentScan的basePackage, 比如<context:component-scan base-package='tech.pdai.springframework'>, 或者@ComponentScan("tech.pdai.springframework")注解,或者 new AnnotationConfigApplicationContext("tech.pdai.springframework")指定扫描的basePackage.

@Service
public class UserServiceImpl {

    /**
     * user dao impl.
     */
    @Autowired
    private UserDaoImpl userDao;

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return userDao.findUserList();
    }

}

依赖注入的三种方式

常用的注入方式主要有三种:构造方法注入(Construct注入),setter注入,基于注解的注入(接口注入)

构造方法注入

在 XML配置中,<constructor-arg>是通过构造函数参数注入

<?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">
    <!-- services -->
    <bean id="userService" class="tech.pdai.springframework.service.UserServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions for services go here -->
</beans>

本质上是new UserServiceImpl(userDao)创建对象, 所以对应的service类是这样的:

public class UserServiceImpl {

    /**
     * user dao impl.
     */
    private final UserDaoImpl userDao;

    /**
     * init.
     * @param userDaoImpl user dao impl
     */
    public UserServiceImpl(UserDaoImpl userDaoImpl) {
        this.userDao = userDaoImpl;
    }

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return this.userDao.findUserList();
    }
}

在注解和Java配置方式下

@Service
public class UserServiceImpl {

    /**
     * user dao impl.
     */
    private final UserDaoImpl userDao;

    /**
     * init.
     * @param userDaoImpl user dao impl
     */
    @Autowired // 这里@Autowired也可以省略
    public UserServiceImpl(final UserDaoImpl userDaoImpl) {
        this.userDao = userDaoImpl;
    }

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return this.userDao.findUserList();
    }
}

setter方式

在XML配置方式中,property都是setter方式注入

<?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">
    <!-- services -->
    <bean id="userService" class="tech.pdai.springframework.service.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>
    <!-- more bean definitions for services go here -->
</beans>

本质上包含两步:

  1. 第一步,需要new UserServiceImpl()创建对象, 所以需要默认构造函数
  2. 第二步,调用setUserDao()函数注入userDao的值, 所以需要setUserDao()函数

所以对应的service类是这样的:

public class UserServiceImpl {

    /**
     * user dao impl.
     */
    private UserDaoImpl userDao;

    /**
     * init.
     */
    public UserServiceImpl() {
    }

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return this.userDao.findUserList();
    }

    /**
     * set dao.
     *
     * @param userDao user dao
     */
    public void setUserDao(UserDaoImpl userDao) {
        this.userDao = userDao;
    }
}

在注解和Java配置方式下

public class UserServiceImpl {

    /**
     * user dao impl.
     */
    private UserDaoImpl userDao;

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return this.userDao.findUserList();
    }

    /**
     * set dao.
     *
     * @param userDao user dao
     */
    @Autowired
    public void setUserDao(UserDaoImpl userDao) {
        this.userDao = userDao;
    }
}

注解注入

以@Autowired(自动注入)注解注入为例,修饰符有三个属性:Constructor,byType,byName。默认按照byType注入。

@Service
public class UserServiceImpl {

    /**
     * user dao impl.
     */
    @Autowired
    private UserDaoImpl userDao;

    /**
     * find user list.
     *
     * @return user list
     */
    public List<User> findUserList() {
        return userDao.findUserList();
    }
}

补:AOP

posted @   柯文先生  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示