Spring 学习笔记

2020-01-11  本文已影响0人  rayma

Spring笔记

1、Spring概况

Spring雏形:interface21
下载地址:https://repo.spring.io/release/org/springframework/spring/

1.1、Spring的优点

1.2、Spring的本质

1.3、Spring Maven 依赖

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

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

1.4、组成(七大模块)

1.4、七大模块.png

通过配置管理特性、AOP模块直接面向切面的棉城功能集成到了Spring框架中。所以,可以很容

易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中

的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务

管理集成到应用程序中。

JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应

商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量

(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。

Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、

Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。

Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所

以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求

参数绑定到域对象的工作。

MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为

高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

2、控制反转(IoC)

2.1、IoC的本质

​ IoC (Inversion of Control 控制反转)是一种设计思想,通过描述 (XML或注解) 并通过第三方去生成或获取特定对象的方式,在Spring中实现控制反转的是IoC容器,DI (Dependency Injection 依赖注入)是实现IoC的一种方式。

2.1_1.png

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

2.1_2.png

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

2.2、如何理解IoC控制反转

2.3、IoC能做什么

​ IoC不是一种技术,只是一种思想,一个重要的面对对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合、难测试,有了IOC容器后,把创建和查找依赖对象的控制权交给容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,也方便测试,利于功能复用,使得程序体系架构变得灵活。

​ 其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。

​ IoC很好的体现了面向对象设计法则之一 ———— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

2.4、实例与创建对象的方式

2.4.1、 IoC原型实例

1、先写一个UserDao接口

public interface UserDao {
    public void getUser();
}

2、再写Dao的实现类

public class UserDaoImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("获取用户数据");
    }
}

3、然后写UserService的接口

public interface UserService {
    public void getUser();
}

4、最后写Service的实现类

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();
    @Override
    public void getUser() {
        userDao.getUser();
    }
}

5、测试

@Test
public void test(){
    UserService service = new UserServiceImpl();
    service.getUser();
}

以上是传统Java SE程序的写法,修改如下:

增加一个Userdao的实现类 UserDaoMySqlImpl.java

public class UserDaoMySqlImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("MySql获取用户数据");
    }
}

要使用MySql时 , 就需要去service实现类里面修改对应的实现

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoMySqlImpl();
    @Override
    public void getUser() {
        userDao.getUser();
    }
}

假设, 再增加一个Userdao 的实现类 UserDaoOracleImpl.java

public class UserDaoOracleImpl implements UserDao {
    @Override
    public void getUser() {
        System.out.println("Oracle获取用户数据");
    }
}

​ 这时要调用Oracle, 就需要去service实现类里面修改对应的实现,如果类似的需求非常多,这种方式便不适用,耦合性非常高。

如何解决 ?

利用set方法 , 修改代码如下:

public class UserServiceImpl implements UserService {
    private UserDao userDao;
    // 利用set实现
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    
    @Override
    public void getUser() {
        userDao.getUser();
    }
}

修改测试类

@Test
public void test(){
    UserServiceImpl service = new UserServiceImpl();
    service.setUserDao(new UserDaoMySqlImpl());
    service.getUser();
    //如果又想用Oracle去实现呢?
    service.setUserDao(new UserDaoOracleImpl());
    service.getUser();
}

​ 区别:在使用set方法之前,所有需要的对象都是由程序员主动创建,使用set方法注入之后,主动权由程序员转移到了调用者,程序员不用再理会对象的创建,可以更专注业务的实现,耦合性大大降低,这便是IoC 的原型。

2.4.2、第一个Spring程序

1、导入Maven依赖项

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

2、编写User实体类 User.java

public class User {
    private String name;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "User{" +"name='" + name + '\'' +'}';
    }
}

3、编写spring配置文件 applicationContext.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 name="user" class="com.woitumi.springtest.User">
        <property name="name" value="woitumi"/>
    </bean>
</beans>

4、创建测试类

public class UserTest {
    public static void main(String[] args){
        //解析applicationContext.xml文件,生成并管理相应的Bean对象
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //getBean的参数为spring配置文件中bean的id
        User user = (User) applicationContext.getBean("user");
        System.out.println(user.toString());
    }
}

输出


2.4.2_OUTPUT.png

扩展
在上述IoC原型实例中新增加Spring配置文件 applicationContext.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="MysqlImpl" class="UserDaoMySqlImpl"/>
    <bean id="OracleImpl" class="UserDaoOracleImpl"/>
    <bean id="ServiceImpl" class="UserServiceImpl">
        <!--注意: 这里的name并不是属性 , 而是set方法名字后半部分 , 首字母小写-->
        <!--引用另外一个bean , 不是用value 而是用 ref-->
        <property name="userDao" ref="OracleImpl"/>
    </bean>
</beans>

测试

@Test
public void test2(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserServiceImpl serviceImpl = (UserServiceImpl) context.getBean("ServiceImpl");
    serviceImpl.getUser();
}

如此一来就实现了解耦,要实现不同的操作,只需要在xml配置文件中修改配置。

2.4.3、IoC创建对象的方式

2.4.3.1、通过无参构造方法

1、修改 2.4.2第一个spring程序 实例中 User 类,增加User显式无参构造方法,其他不变

public class User {
    private String name;
    public User() {
        System.out.println("user无参构造方法");
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

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

2、测试类

public class UserTest {
    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) applicationContext.getBean("user");
        System.out.println(user.toString());
    }
}

输出

2.4.3_OUTPUT.png

结论:在执行 getBean 时,user对象已经创建好。(通过无参构造)

2.4.3.2、通过有参构造方法

1、修改 第一个spring程序 实例中 User 类,增加User有参构造方法

public class User {
    private String name;
    public User(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

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

2、修改applicationContext.xml配置文件,针对有参构造方法有三种配置方式

 <!-- 第一种根据index参数下标设置 -->  
    <bean name="user" class="com.woitumi.springtest.User">
         <!-- index指构造方法 , 下标从0开始 -->
        <constructor-arg index="0" value="woitumi"/>
    </bean>
<!-- 第二种根据参数名字设置,name属性值必须与set方法后面的名字一样--> 
    <bean name="user" class="com.woitumi.springtest.User">
        <!-- name指参数名 -->
        <constructor-arg name="name" value="woitumi"/>
    </bean>
 <!-- 第三种根据参数类型设置,需确保参数的类型全局唯一 -->  
    <bean name="user" class="com.woitumi.springtest.User">
        <constructor-arg type="java.lang.String" value="woitumi"/>
    </bean>

3、测试类

public class UserTest {
    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) applicationContext.getBean("user");
        System.out.println(user.toString());
    }
}

输出


2.5_OUTPUT.png

结论:管理的对象在配置文件加载的时候初始化完成。

2.5、依赖注入(DI)

概念
DI (Dependency Injection) 依赖注入:组件之间的依赖关系由容器在运行期决定,也就是说,由容器动态将某个依赖关系注入到组件中。依赖注入并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过注入机制,只需要通过简单的配置,无需任何代码就可以指定目标需要的资源,完成自身的业务逻辑,不需要关心具体的资源来自何处,由谁实现。

​IoC在系统运行中,会动态的向某个对象提供它(对象)所需的其他对象,这一点是通过DI(依赖注入)来实现的,比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

如何理解DI?

IoC Service Provider 为被注入对象提供被依赖对象有如下几种方式

2.5.1、构造器注入

<font color=red>(参考 2.4.3.1 跟 2.4.3.2 实例)</font>

2.5.2、set方法注入

要求被注入的属性必须有set方法,set方法的方法名由 set + 属性名首字母大写(如果属性的类型为boolean,则为 is + 属性名首字母大写)。

实例:
Address.java

public class Address {
    private String address;
    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

Student.java

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobby;
    private Map<String, String> card;
    private Set<String> games;
    private String wife;
    private Properties info;

    /**省略 getter 跟 setter 方法**/
    
    public void show() {
        StringBuilder sb = new StringBuilder()
                .append("name = ").append(name).append("\n")
                .append("address = ").append(address.getAddress()).append("\n")
                .append("hobby = ").append(hobby).append("\n")
                .append("card = ").append(card).append("\n")
                .append("games = ").append(games).append("\n")
                .append("wife = ").append(wife).append("\n")
                .append("info = ").append(info).append("\n")
                .append("books = ");
        if (books != null)
            for (String book : books)
                sb.append(String.format("《%s》 ", book));
        System.out.println(sb.toString());
    }
}

1、常量注入

    <bean name="student" class="com.woitumi.springtest.Student">
        <property name="name" value="张三"/>
    </bean>

2、Bean注入 ( ref 引用 )

    <bean id="addr" class="com.woitumi.springtest.Address">
        <property name="address" value="广州"/>
    </bean>
    <bean name="student" class="com.woitumi.springtest.Student">
        <property name="name" value="张三"/>
        <property name="address" ref="addr"/>
    </bean>

3、数组注入

 <property name="books">
      <array>
            <value>Java从入门到夺门而出</value>
            <value>Spring概述</value>
      </array>
 </property>

4、List注入

<property name="hobbys">
     <list>
          <value>敲代码</value>
          <value>看影视</value>
     </list>
 </property>

5、Map注入

  <property name="card">
       <map>
           <entry key="CBC" value="54221457635512423"/>
           <entry key="BC" value="48526365765123651"/>
        </map>
  </property>

6、Set注入

<property name="games">
      <set>
          <value>LOL</value>
          <value>csgo</value>
      </set>
</property>

7、Null注入

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

8、Properties注入

<property name="info">
     <props>
          <prop key="学号">20191219001</prop>
          <prop key="性别">男</prop>
     </props>
 </property>

测试类

   @Test
    public void diTest(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) context.getBean("student");
        student.show();
    }

输出


2.5.3_OUTPUT_1.png

2.5.3、自动装配

​ 自动装配(autowire)是使用Spring满足bean依赖的一种方式,可以在applicationContext.xml配置文件的bean标签添加 autowire 属性,使得Spring可以自动在上下文给对象注入属性。

自动装配做的两件事:

Spring中bean有三种装配机制:

自动装配方式:

实例环境

public class Cat {
    public void shout() {
        System.out.println("miao~");
    }
}
public class Dog {
    public void shout() {
        System.out.println("wang~");
    }
}
public class User {
    private String name;
    private Cat cat;
    private Dog dog;
    /**省略 getter 跟 setter 方法**/
}
    <bean id="dog" class="com.woitumi.autowire.Dog"/>
    <bean id="cat" class="com.woitumi.autowire.Cat"/>
    <bean id="user" class="com.woitumi.autowire.User">
        <property name="name" value="woitumi"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>
public class AutowireTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) context.getBean("user");
        System.out.println(String.format("%s的宠物", user.getName()));
        user.getCat().shout();
        user.getDog().shout();
    }
}

输出


2.5.3_OUTPUT_2.png
2.5.3.1、byName

1、修改applicationContext.xml配置文件中的bean如下:

    <bean id="dog" class="com.woitumi.autowire.Dog"/>
    <bean id="cat" class="com.woitumi.autowire.Cat"/>
    <bean id="user" class="com.woitumi.autowire.User" autowire="byName">
        <property name="name" value="woitumi"/>
    </bean>

2、测试输出


2.5.3.1_OUTPUT_1.png

3、继以上修改再将cat 的bean id 改为 catXXX

    <bean id="dog" class="com.woitumi.autowire.Dog"/>
    <bean id="cat1122" class="com.woitumi.autowire.Cat"/>
    <bean id="user" class="com.woitumi.autowire.User" autowire="byName">
        <property name="name" value="woitumi"/>
    </bean>

4、测试输出 (空指针异常)


2.5.3.1_OUTPUT_2.png

使用byName小结:
<font color="red">byName属性会自动在上下文里找和自己属性对应set方法后边的名字(首字母改为小写)一样的bean id。若bean id 跟set方法后面的名字不一样,则报空指针异常。(id必须唯一)</font>

2.5.3.2、byType

1、修改xml配置文件如下

    <bean id="dog" class="com.woitumi.autowire.Dog"/>
    <bean id="cat" class="com.woitumi.autowire.Cat"/>
    <bean id="user" class="com.woitumi.autowire.User" autowire="byType">
        <property name="name" value="woitumi"/>
    </bean>

2、测试输出(正常)

3、继续修改xml配置文件如下:

   <bean id="dog" class="com.woitumi.autowire.Dog"/>
    <bean id="cat" class="com.woitumi.autowire.Cat"/>
    <bean id="cat2" class="com.woitumi.autowire.Cat"/>
    <bean id="user" class="com.woitumi.autowire.User" autowire="byType">
        <property name="name" value="woitumi"/>
    </bean>

4、测试输出(NoUniqueBeanDefinitionException 异常)

5、继续修改xml配置文件如下:

    <bean id="dog" class="com.woitumi.autowire.Dog"/>
    <bean id="cat1122" class="com.woitumi.autowire.Cat"/>
    <bean id="user" class="com.woitumi.autowire.User" autowire="byType">
        <property name="name" value="woitumi"/>
    </bean>

6、测试输出 (正常)

使用byType小结:
<font color="red">byType会自动在上下文里找和自己属性类型相同的bean,类型要唯一,要被注入的bean没有id值也可以,class要唯一。</font>

2.5.3.3、constructor

1、修改xml配置文件如下:

    <bean id="dog" class="com.woitumi.autowire.Dog"/>
    <bean id="cat" class="com.woitumi.autowire.Cat"/>
    <bean id="user" class="com.woitumi.autowire.User" autowire="constructor">
        <property name="name" value="woitumi"/>
    </bean>

2、修改User.java,添加构造方法,如下:

public class User {
    private String name;
    private Cat cat;
    private Dog dog;

    public User(Cat cat, Dog dog) {
        this.cat = cat;
        this.dog = dog;
    }
    /**省略 getter 跟 setter 方法**/
}

3、测试输出


2.5.3.3_OUTPUT.png

使用constructor小结:
<font color="red">constructor会尝试把它的构造函数的参数与配置文件中 beans 名称中的一个进行匹配和连线。如果找到匹配项,它会注入这些 bean,否则,会抛出异常。</font>

2.5.4、注解注入

以2.5.3实例继续

2.5.4.1、@Autowired

@Autowired 注解表示按照类型自动装配(属于Spring规范),不支持id匹配。可以写在字段上,也可以写在setter方法上(使用@Autowired 注解可以省略set方法)。默认情况下要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false,如:@Autowired(required=false) 。

1、在spring配置文件中引入context文件头

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

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

2、开启属性注解支持

<context:annotation-config/>

3、修改后完整的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.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">

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

    <bean id="dog" class="com.woitumi.autowire.Dog"/>
    <bean id="cat" class="com.woitumi.autowire.Cat"/>
    <bean id="user" class="com.woitumi.autowire.User" >
        <property name="name" value="woitumi"/>
    </bean>
</beans>

4、修改User.java,将set方法去掉,添加 @Autowired 注解

public class User {
    private String name;
    @Autowired
    private Cat cat;
    @Autowired(required = false) //如果允许对象为null,可以设置required为false,默认为true
    private Dog dog;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Cat getCat() {
        return cat;
    }
    public Dog getDog() {
        return dog;
    }
}

5、测试输出


2.5.4.1_OUTPUT.png
2.5.4.2、@Qualifier

@Qualifier 不能单独使用,需跟 @Autowired 配合使用,两者配合使用后可根据 byName 的方式来自动装配。

1、修改xml配置文件中的bean id不为类的默认名字,如下:

 <bean id="dog11" class="com.woitumi.autowire.Dog"/>
 <bean id="cat22" class="com.woitumi.autowire.Cat"/>

2、在User.java类中添加 @Qualifier 注解

    @Autowired
    @Qualifier(value = "cat22")
    private Cat cat;
    @Autowired(required = false) //如果允许对象为null,可以设置required为false,默认为true
    @Qualifier(value = "dog11")

3、测试输出成功

2.5.4.3、@Resource

@Resource 注解(属于J2EE规范)为 @Autowired 和 @Qualifier 的结合版。先尝试以 byName 方式对属性进行查找装配,若不成功,则以 byType 的方式进行装配,若两者都不成功,则报异常。

@Resource 注解 默认按照名称匹配,名称可以通过name属性指定,如果没指定,当注解写在字段上时,默认取字段名进行查找,注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

@Autowired先byType,@Resource先byName。

1、修改xml配置文件,如下:

    <bean id="dog" class="com.woitumi.autowire.Dog"/>
    <bean id="cat22" class="com.woitumi.autowire.Cat"/>
    <bean id="cat33" class="com.woitumi.autowire.Cat"/>

2、修改User.java类

    @Resource(name = "cat22")
    private Cat cat;
    @Resource
    private Dog dog;

3、测试输出成功
4、再修改xml配置文件如下:

    <bean id="dog" class="com.woitumi.autowire.Dog"/>
    <bean id="cat22" class="com.woitumi.autowire.Cat"/>

5、修改User.java类,只保留 @Resource 注解

    @Resource
    private Cat cat;
    @Resource
    private Dog dog;

6、测试输出成功

2.6、Bean部分配置说明

别名
alias 设置别名 , 为bean设置别名 , 可以设置多个别名

<!--设置别名:在获取Bean的时候可以使用别名userNew获取-->
<alias name="user" alias="userNew"/>
<!--bean就是java对象,由Spring创建和管理-->
<!--
    id 是bean的标识符,要唯一,如果没有配置id,name就是默认标识符
    如果配置id,又配置了name,那么name是别名
    name可以设置多个别名,可以用逗号,分号,空格隔开
    如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
    class是bean的全限定名=包名+类名
-->
<bean id="user" name="user2 u2,u3;u4" class="com.woitumi.springtest.User">
    <property name="name" value="woitumi"/>
</bean>

2.7、Bean的作用域

​ 在Spring中,组成应用程序的主题及有Spring IoC容器所管理的对象,被称为Bean。换言之,Bean就是由IoC容器初始化、装配以及管理的对象。

Bean的作用域如下表:


2.7.png

以上4种作用域中的request跟session只能用在基于web的Spring ApplicationContext环境中。

2.7.1、Singleton

User.java

public class User {
    private String name;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "User{" +"name='" + name + '\'' +'}';
    }
}

在xml配置文件中将bean定义成Singleton

  <bean name="user" class="com.woitumi.springtest.User" scope="singleton">
       ... ...
  </bean>

测试

public class UserTest {
    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) applicationContext.getBean("user");
        User user2 = (User) applicationContext.getBean("user");
        System.out.println(user == user2);
    }
}

输出


2.7.1_OUTPUT.png

2.7.2、Prototype

在xml配置文件中将bean定义成Prototype

  <bean name="user" class="com.woitumi.springtest.User" scope="prototype">
       ... ...
  </bean>
public class UserTest {
    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) applicationContext.getBean("user");
        User user2 = (User) applicationContext.getBean("user");
        System.out.println(user == user2);
    }
}

测试输出


2.7.2_OUTPUT.png

2.7.3、Request

在xml配置文件中将bean定义成Request

  <bean name="user" class="com.woitumi.springtest.User" scope="request">
       ... ...
  </bean>

2.7.4、Session

在xml配置文件中将bean定义成Session

  <bean name="user" class="com.woitumi.springtest.User" scope="session">
       ... ...
  </bean>

​ 针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与Request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域的bean也会被废弃掉。

3、Spring注解式开发

Spring 在 4.0 版本后引入了全注解开发模式,去除了编写繁琐的配置文件,使用配置类即可进行bean的扫描和装配。

3.1、简单使用和说明

步骤

1、使用注解模式必须先确保 aop包 的引入:


3.1_aop包引入.png

2、在配置文件引入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
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
</beans>

3、配置扫描哪些包下的注解:

<context:component-scan base-package="[包名]"/>

4、使用和说明

<?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
       http://www.springframework.org/schema/context/spring-context-3.0.xsd">
       <!-- 指定注解扫描包 -->
       <context:component-scan base-package="com.woitumi.springtest"/>
</beans>
/**
@Component 注解将此类标注为Spring的一个组件,并导入到IoC容器中。
此处相当于在xml配置文件中 <bean name="cat" class="com.woitumi.springtest.Cat">
为了更好的分层,Spring可以使用其他三个注解(功能一样),分别是:
    @Controller     对Web层实现类标注
    @Service        对Service层实现类标注
    @Repository     对Dao层实现类标注
*/
@Component("cat") 
public class Cat {
    public void shout(){
        System.out.println("miao~");
    }
}
@Component("dog")
public class Dog {
    public void shout(){
        System.out.println("wang~");
    }
}
@Component("user") //@Component("值") 注解将此类标注为Spring的一个组件,值相当于XML配置文件中的name属性。
@Scope("Singleton") //可用 @Scope 注解标注 Singleton 或 Prototype 模式
public class User {
   // @Value注解相当于配置文件中 <property name="name" value="woitumi">,使用@Value注解可以不需要set方法。
    @Value("woitumi") 
    private String name;
    private int count;
    @Autowired // @Autowired按照类型自动装配,也可使用 @Resource("cat")指定id 
    private Cat cat;
    @Autowired
    private Dog dog;

    public String getName() {
        return name;
    }

    public Cat getCat() {
        return cat;
    }

    public Dog getDog() {
        return dog;
    }
    
    @Value("2")  // 如果有set方法也可以将 @Value("值") 设在set方法上。
    public void setCount(int count) {
        this.count = count;
    }

    public int getCount() {
        return count;
    }
}
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) context.getBean("user");
        System.out.println(String.format("%s有%d只宠物", user.getName(), user.getCount()));
        user.getCat().shout();
        user.getDog().shout();
    }
}

输出


3.1_OUTPUT.png

XML配置文件与注解装配比较
1、XML可以适用于任何场景,结构清晰,维护方便。
2、注解开发简单方便,但在非自己写的类里难以使用。

3.2、基于 JavaConfig 配置

​ JavaConfig 原来是 Spring 的一个子项目,它通过 Java 类的方式提供 Bean 的定义信息,在 Spring4 版本后, JavaConfig 已正式成为 Spring 的核心功能 。

User.java类

public class User {
    private String name;
    private Car car;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }
}

Car.java类 :

public class Car {
    private String brand;
    private String type;
    //使用构造方法注入值
    public Car(String brand, String type) {
        this.brand = brand;
        this.type = type;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

新建一个config配置包,编写一个MyConfig配置类,如下:

@Configuration //代表这是配置类
public class MyConfig {  
    @Bean("myCar")  //@Bean("myCar") 注解会向容器中注册一个叫 myCar 的对象
    public Car getCar() {
        return new Car("保时捷", "911");
    }
    
    //向容器中注册装配一个 user 对象,并通过byType的方式注入car,对象car需要有set方法
    @Bean(value = "user", autowire = Autowire.BY_TYPE)
    public User getUser() {
        User user = new User();
        user.setName("woitumi");
        return user;
    }
}

写法二:

public class User {
    @Value("woitumi") //使用  @Value("值") 注解将值注入,相应的字段可以不需要set方法
    private String name;
    private Car car;

    public String getName() {
        return name;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }
}
public class Car {
    @Value("保时捷") //使用  @Value("值") 注解将值注入,可以不需要set方法
    private String brand;
    @Value("911")
    private String type;
    
    public String getBrand() {
        return brand;
    }
    
    public String getType() {
        return type;
    } 
}
@Configuration //@Configuration 代表这是配置类
// @ComponentScan("com.woitumi.springtest") //指定扫描包 (了解可以这么用)
// @Import(MyConfig2.class) //融合多个配置类
public class MyConfig {
    @Bean("myCar")  //@Bean("myCar") 注解会向容器中注册一个叫 myCar 的对象
    public Car getCar() {
        return new Car();
    }
 //向容器中注册装配一个 user 对象,并通过byType的方式注入car,对象car需要有set方法
    @Bean(value = "user", autowire = Autowire.BY_TYPE)
    public User getUser() {
        return new User();
    }
}

测试类

    @org.junit.Test
    public void test() {
        ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        User user = context.getBean("user", User.class);
        System.out.println(String.format("%s的车是%s%s",
                user.getName(),
                user.getCar().getBrand(),
                user.getCar().getType()
        ));
    }

输出


3.2_OUTPUT.png

4、代理模式

​ 为什么要学习代理模式?因为AOP的底层机制是动态代理。

​ 代理模式作为23种经典设计模式之一,其比较官方的定义为“为其他对象提供一种代理以控制对这个对象的访问”,简单点说就是,之前A类自己做一件事,在使用代理之后,A类不直接去做,而是由A类的代理类B来去做。代理类其实是在之前类的基础上做了一层封装。

它的设计思路是:定义一个抽象角色,让代理角色和真实角色分别去实现它。

代理模式的核心作用

角色分类

代理模式分为

4.1、静态代理

什么是静态代理?

静态代理的缺点

实例一

Rent.java 抽象角色

//抽象角色:租房
public interface Rent {
    public void rent();
}

Host.java 真实角色

//真实角色: 房东,房东要出租房子
public class Host implements Rent{
    public void rent() {
        System.out.println("房屋出租");
    }
}

Proxy.java 代理角色

//代理角色:中介
public class Proxy implements Rent {
    private Host host;
    public Proxy() {
    }
    public Proxy(Host host) {
        this.host = host;
    }

    //租房
    public void rent(){
        seeHouse();
        host.rent();
        fare();
    }
    //看房
    public void seeHouse(){
        System.out.println("带房客看房");
    }
    //收中介费
    public void fare(){
        System.out.println("收中介费");
    }
}

Client.java 客户

//租客通过中介租到房东的房子
public class Client {
    public static void main(String[] args) {
        //房东要租房
        Host host = new Host();
        //中介帮助房东
        Proxy proxy = new Proxy(host);
        //租客找中介
        proxy.rent();
    }

分析: 在这个过程中,客户直接接触的是中介,就如同现实生活中的样子,你看不到房东,但是客户通过中介租到了房东的房子,这就是静态代理模式。

实例二

1、创建抽象角色,比如增删改查的业务逻辑

//抽象角色:增删改查业务
public interface UserService {
    void add();
    void delete();
    void update();
    void query();
}

2、需要一个真实对象来完成增删改查操作

//真实对象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加");
    }

    public void delete() {
        System.out.println("删除");
    }

    public void update() {
        System.out.println("更新");
    }

    public void query() {
        System.out.println("查询");
    }
}

3、如果需要增加一个日志功能,如何实现?
思路一:在实现类上增加代码。(麻烦)
思路二:使用代理,在不改变原有业务逻辑的情况下实现日志功能

4、定义一个代理角色来实现日志功能

//代理角色,在这里面增加日志的实现
public class UserServiceProxy implements UserService {
    private UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    public void add() {
        log("add");
        userService.add();
    }

    public void delete() {
        log("delete");
        userService.delete();
    }

    public void update() {
        log("update");
        userService.update();
    }

    public void query() {
        log("query");
        userService.query();
    }

    public void log(String msg){
        System.out.println("执行了" + msg + "方法");
    }

}

5、测试类

public class Client {
    public static void main(String[] args) {
        //真实业务
        UserServiceImpl userService = new UserServiceImpl();
        //代理类
        UserServiceProxy proxy = new UserServiceProxy();
        //使用代理类实现日志功能
        proxy.setUserService(userService);
        proxy.add();
    }
}

4.2、动态代理

动态代理的特点

动态代理的好处

动态代理分为

JDK代理和CGLib代理的区别

动态代理的实现步骤

JDK动态代理实例一 :

以 4.1租房实例 继续

//抽象角色:租房
public interface Rent {
    public void rent();
}

Host . java 真实角色

//真实角色: 房东,房东要出租房子
public class Host implements Rent{
    public void rent() {
        System.out.println("房屋出租");
    }
}

ProxyInvocationHandler. java 即代理角色

public class ProxyInvocationHandler implements InvocationHandler {
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成代理类,第二个参数获取要代理的抽象角色
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),this);
    }

    // proxy : 代理类 method : 代理类的调用处理程序的方法对象.
    // 处理代理实例上的方法调用并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        //核心:利用反射实现
        Object result = method.invoke(rent, args);
        fare();
        return result;
    }

    public void seeHouse(){
        System.out.println("带房客看房");
    }

    public void fare(){
        System.out.println("收中介费");
    }

}

Client . java

//租客
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();
    }
}

JDK动态代理实例二:

//用户管理接口
public interface UserManager {
    void addUser(String name, String password);
    void delUser(String name);
}
 //用户管理实现类,实现用户管理接口
public class UserManagerImpl implements UserManager {
    public void addUser(String name, String password) {
        System.out.println("addUser:" + name + ":" + password);
    }

    public void delUser(String name) {
        System.out.println("delUser:" + name);
    }
}
//JDK动态代理实现InvocationHandler接口
public class JdkProxy implements InvocationHandler {
    private Object target;
    //定义获取代理对象方法
    public Object getJdkProxy(Object object) {
        target = object;
        //JDK动态代理只能针对实现了接口的类进行代理
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                                      target.getClass().getInterfaces(), this);
    }
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理,重写invoke方法调用被代理者的方法");
        Object result = method.invoke(target,args);
        System.out.println("JDK动态代理,可以执行附加操作");
        return result;
    }
}

    @Test
    public void JdkProxy() {
        JdkProxy jdkProxy = new JdkProxy(); //实例化JdkProxy对象
        //获取代理对象
        UserManager userManager = (UserManager) jdkProxy.getJdkProxy(new UserManagerImpl());
        userManager.addUser("woitumi", "wanantumi");
 }
4.2_OUTPUT_1.png

CGLib动态代理实例

首先需要导入asm跟cglib的 Maven依赖

        <!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
        <dependency>
            <groupId>org.ow2.asm</groupId>
            <artifactId>asm</artifactId>
            <version>7.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
public class CglibProxy implements MethodInterceptor {
    private Object target;
    //重写拦截方法
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) 
        throws Throwable {
        System.out.println("CGLib动态代理,重写invoke方法调用被代理者的方法");
        Object result = method.invoke(target, objects);
        System.out.println("CGLib动态代理,可以执行附加操作");
        return result;
    }

    //定义获取代理对象方法
    public Object getCglibProxy(Object object) {
        target = object;
        Enhancer enhancer = new Enhancer();
        //设置父类,Cglig是针对指定的类生成一个子类,所以需要指定父类
        enhancer.setSuperclass(object.getClass());
        enhancer.setCallback(this); //设置回调
        return enhancer.create(); //创建并返回代理对象
    }
}
    @Test
    public void CglibProxy() {
        CglibProxy cglibProxy = new CglibProxy();
        UserManager userManager = (UserManager) cglibProxy.getCglibProxy(new UserManagerImpl());
        userManager.delUser("张三");
    }
4.2_OUTPUT_2.png

扩展
实现JDK动态代理的工具类

public class ProxyInvocationHandler implements InvocationHandler { 
    private Object object;
    /**
     * 创建代理对象
     * @param object 被代理者
     * @return 代理者
     */
    public Object createProxy(Object object) {
        this.object = object;
        //Proxy.newProxyInstance创建动态代理的对象,传入被代理对象的类加载器,接口,InvocationHandler对象
        //注意 Proxy 是 java.lang.reflect.Proxy 包下的 Proxy
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), 
                                      object.getClass().getInterfaces(), this);
    }

    //调用被代理者方法,同时可以添加新操作
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //TODO 调用被代理者的方法
        Object result = method.invoke(object, args);
        //TODO 可添加新的操作
        return result;
    }
}

使用(如上述租房实例)

    @Test
    public void rentHost() {
        Host host = new Host();
        Rent proxy = (Rent) new ProxyInvocationHandler().createProxy(host);
        proxy.rent();
    }

5、面向切面编程(AOP)

5.1、AOP概述

​ AOP(Aspect Oriented Programming)意为:面向切面编程,AOP采用一种称为“横切”的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置上。面向切面编程是一种编程范式,它作为OOP面向对象编程的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、权限控制、缓存控制、日志打印等等。AOP采取横向抽取机制,取代了传统纵向继承体系的重复性代码。

AOP把软件的功能模块分为两个部分:

​ 业务处理的主要功能为核心关注点,需要拓展的功能为横切关注点,利用AOP可以对业务逻辑的各个关注点进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,提高开发效率。

5.png

​ 例如,在一个业务系统中,用户登录是基础功能,凡是涉及到用户的业务流程都要求用户进行系统登录。如果把用户登录功能代码写入到每个业务流程中,会造成代码冗余,维护也非常麻烦,当需要修改用户登录功能时,就需要修改每个业务流程的用户登录代码,这种处理方式显然是不可取的。比较好的做法是把用户登录功能抽取出来,形成独立的模块,当业务流程需要用户登录时,系统自动把登录功能切入到业务流程中,以下为用户登录功能切入到业务流程示意图:

5_1.png

AOP相关概念

5.1_2.png

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

5.1_3.png

5.2、OOP与AOP对比理解

OOP (Object Oriented Programming) 面向对象编程,AOP (Aspect Oriented Programming) 面向切面编程。
纵向关系OOP,横向角度AOP

举个小例子:

设计一个日志打印模块。按 OOP 思想,我们会设计一个打印日志 LogUtils 类,然后在需要打印的地方引用即可。

public class ClassA {
    private void initView() {
        Log.d(TAG, "onInitView");
    }
}

public class ClassB {
    private void onDataComplete(Bean bean) {
        Log.d(TAG, bean.attribute);
    }
}

public class ClassC {
    private void onError() {
        Log.e(TAG, "onError");
    }
}

看起来没有任何问题是吧?

但是这个类是横跨并嵌入众多模块里的,在各个模块里分散得很厉害,到处都能见到。从对象组织角度来讲,我们一般采用的分类方法都是使用类似生物学分类的方法,以「继承」关系为主线,我们称之为纵向,也就是 OOP。设计时只使用 OOP思想可能会带来两个问题:

  1. 对象设计的时候一般都是纵向思维,如果这个时候考虑这些不同类对象的共性,不仅会增加设计的难度和复杂性,还会造成类的接口过多而难以维护(共性越多,意味着接口契约越多)。
  2. 需要对现有的对象 动态增加 某种行为或责任时非常困难。

而AOP就可以很好地解决以上的问题,怎么做到的?除了这种纵向分类之外,我们从横向的角度去观察这些对象,无需再去到处调用 Log 打印日志了,声明哪些地方需要打印日志,这个地方就是一个切面,AOP 会在适当的时机把打印语句插进切面。

// 只需要声明哪些方法需要打印 log,打印什么内容
public class ClassA {
    @Log(msg = "onInitView")
    private void initView() {
    }
}

public class ClassB {
    @Log(msg = "bean.attribute")
    private void onDataComplete(Bean bean) {
    }
}

public class ClassC {
    @Log(msg = "onError")
    private void onError() {
    }
}

​ 如果说 OOP 是把问题划分到单个模块的话,那么 AOP 就是把涉及到众多模块的某一类问题进行统一管理。AOP的目标是把这些功能集中起来,放到一个统一的地方来控制和管理。利用 AOP 思想,这样对业务逻辑的各个部分进行了隔离,从而降低业务逻辑各部分之间的耦合,提高程序的可重用性,提高开发效率。


5.2.png

OOP 与 AOP 的区别

OOP 与 AOP 两者是一个相互补充和完善的关系。

5.3、使用

使用AOP织入,需要导入 aspectjweaver 依赖包

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

在applicationContext.xml文件中配置AOP需要引入AOP约束,如下:

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
5.3.1、通过Spring API实现
//业务接口
public interface DataManager {
    public void add();
    public void delete();
    public void update();
    public void query();
}
//业务实现类
public class DataManagerImpl implements DataManager {
    public void add() {
        System.out.println("添加");
    }

    public void delete() {
        System.out.println("删除");
    }

    public void update() {
        System.out.println("更新");
    }

    public void query() {
        System.out.println("查询");
    }
}
//前置增强类
public class BeforeLog implements MethodBeforeAdvice {
    /**
     * @param method 要执行的目标对象的方法
     * @param objects 被调用的方法的参数
     * @param target 目标对象
     */
    public void before(Method method, Object[] objects, Object target) throws Throwable {
        System.out.println(String.format("执行 %s 类的 %s 方法前的日志",
                target.getClass().getName(),
                method.getName()
        ));
    }
}
//后置增强类
public class AfterLog implements AfterReturningAdvice {
    /**
     * @param returnValue 返回值
     * @param method 被调用的方法
     * @param objects 被调用方法的对象参数
     * @param target 被调用的目标对象
     */
    public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) 
        throws Throwable {
        System.out.println(String.format("执行 %s 类的 %s 方法后的日志,返回值为 %s",
                target.getClass().getName(),
                method.getName(),
                returnValue
        ));
    }
}
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 注册bean -->
    <bean id="dataManager" class="com.woitumi.aoptest.DataManagerImpl"/>
    <bean id="beforeLog" class="com.woitumi.aoptest.BeforeLog"/>
    <bean id="afterLog" class="com.woitumi.aoptest.AfterLog"/>
    <!-- aop配置 -->
    <aop:config>
        <!-- 切入点,expression 表达式匹配要执行的方法 -->
        <aop:pointcut id="pointcut" expression="execution(* com.woitumi.aoptest.DataManagerImpl.*(..))"/>
        <!-- 执行环绕,advice-ref 表示执行方法,pointcut-ref 表示切入点 -->
        <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

</beans>
   @Test
    public void aopTest() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        DataManager manager = (DataManager) context.getBean("dataManager");
        manager.add();
    }
5.3.2_PUTPUT.png

附录:execution 表达式含义:

execution(* com.woitumi.aoptest.DataManagerImpl.*(..))
<!--
1、execution() : 表达式主体
2、第一个 * 号: 表示返回类型,* 号表示返回所有类型
3、包名: 表示需要拦截的包名
4、*(..) :* 号表示所有方法,括号内的点表示参数,两个点表示所有参数。
-->
5.3.2、自定义类实现AOP

继上述例子 业务接口跟业务实现类不变,修改增加

//自定义切入类
public class CustomPointcut {
    public void before() {
        System.out.println("before");
    }

    public void after() {
        System.out.println("after");
    }
}
<bean id="custom" class="com.woitumi.aoptest.CustomPointcut"/>
 <aop:config>
    <!-- 使用AOP标签实现 -->
    <aop:aspect ref="custom">
        <aop:pointcut id="customPointcut" expression="execution(*com.woitumi.aoptest.DataManagerImpl.*(..))"/>
        <aop:before method="before" pointcut-ref="customPointcut"/>
        <aop:after method="after" pointcut-ref="customPointcut"/>
    </aop:aspect>
</aop:config>
5.3.2_OUTPUT_2.png
5.3.3、使用注解实现AOP

继以上实例修改增加

@Aspect
public class AnnotationPointcut {
    @Before("execution(* com.woitumi.aoptest.DataManagerImpl.*(..))")
    public void before() {
        System.out.println("before");
    }

    @After("execution(* com.woitumi.aoptest.DataManagerImpl.*(..))")
    public void after() {
        System.out.println("after");
    }

    @Around("execution(* com.woitumi.aoptest.DataManagerImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前");
        System.out.println("签名:" + joinPoint.getSignature());
        //执行目标方法proceed
        Object proceed = joinPoint.proceed();
        System.out.println("环绕后");
        System.out.println(proceed);
    }
}
 <bean id="annotationPointcut" class="com.woitumi.aoptest.AnnotationPointcut"/>
 <aop:aspectj-autoproxy/> 
<!-- 开启注解支持 
通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<aop:aspectj-autoproxy />隐藏起来了 

<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy  poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
-->
5.3.3_OUTPUT.png

6、整合MyBatis

(后续补充)

7、声明式事务

(后续补充)

上一篇下一篇

猜你喜欢

热点阅读