Spring框架核心机制IOC和AOP深入解读spring基础

文章目录


1. Spring框架

1.1 Spring简介


  • Spring就是分层的JavaSE应用full-strack轻量级开源框架,IOC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)
  • 什么是轻量级?一般情况下如果框架api使用比较简单,就叫轻量级

1.2 Spring的架构组成


1.2.1 Spring框架

在这里插入图片描述

最重要的核心容器Core Container:分为4个部分

  • Beans ,容器主要放的就是定义的bean对象
  • Core,核心
  • Context,上下文
  • SpEL,就是Spring的EL表达式

1.2.2 工厂

  1. CoreContariner核心容器,其实是搭建并管理了工厂
  2. 是工厂的设计模式
  3. 工厂中是用来生产项目中一个一个的对象
  4. 因此使用spring最重要的就是搭建工厂
  5. 该工厂加载配置文件:.xml文件,生产配置中记录的对象,我们通过getBean方法调用就可以了

1.3 控制反转


1.3.1 例题讲解


什么是反转控制? 反转了依赖关系的满足方式,由之前的直接创建依赖对象,变为由工厂推送。(变主动为被动,即反转)解决了具有依赖关系的组件之间的强耦合

举例说明

  • 比如UserServiceImpl强耦合了UserDaoImpl,利用了反转控制,可以使UserServiceImpl不再引用任何一个UserDao的实现类例如UserDaoImpl,在需要UserDaoImpl的位置,我们只给一个属性UserDao userDao;并定义set/get方法,允许userDao属性接收spring赋值
public class UserServiceImpl implements UserService{

	private UserDao userDao;
    @Override
    public List<User> queryUser() {
    	List<User> list = userDao.queryUser();
        return list;
    }
    public void setUserDao(UserDao userDao){
    	this.userDao = userDao;
    }
}
1.3.2 先写一个spring-context.xml文件

  • 上面我们说过了,反转控制,那么spring是怎么给UserServiceImpl类的UserDao属性赋值的呢?假设我们赋值的是UserDao的实现类UserDaoImpl
  • 首先,我们在.xml配置文件中定义一个<beans>标签,里面写<bean></bean>
  • <bean>标签的id随便给,id=“userDaoImpl”、class=”com/lyx/dao/impl/UserDaoImpl“,就是UserDaoImpl的路径

在这里插入图片描述

1.3.3 controller层

Control层调用Sevice层的时候,如下调用

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
        UserDaoImpl userDao = (UserDaoImpl)context.getBean("userDaoImpl");
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao);
        userService.queryPerson();

运行结果:这里成功调用了UserDaoImpl;

理解说明

  • Spring框架加载这个.xml配置文件,
  • 怎么加载?ClassPathXmlApplicationContext类加载
 ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
  • ClassPathXmlApplicationContext 是ApplicationContext接口的实现类,它是从类的根路径下加载配置文件

  • ApplicationContext接口,它代表应用上下文,可以通过它的实例获得Spring容器中的Bean对象

  • 我们通过.getBean(”userDaoImpl“)生产对象

  • getBean()方法的参数给bean的id,返回的就是UserDaoImpl类型的对象,类型自己强转就行了

//Object userDaoImpl = context.getBean("userDaoImpl");
UserDaoImpl userDaoImpl = (UserDaoImpl)context.getBean("userDaoImpl");
  • getBean方法的参数也可以是Class类型,这样就不需要强转了,因为你已经告诉它了,但是这种方法只适合只有一个UserDaoImpl,如果还有一个会报错
UserDaoImpl userDaoImpl = context.getBean(UserDaoImpl.class);
  • 当然了,实现功能要先导Spring开发的基本包的坐标:Spring框架用到依赖
  • 还有一点,.xml在项目加载的时候不会被编译到target测试文件里面,需要在pom.xml里面配置一个<build>
  <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>*.xml</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
1.3.4 bean标签

    <bean id="" class="" scope="" init-method="" destroy-method=""/>

id、class上面说过了

1.3.4.1 饿汉式、懒汉式

scope


  1. singleton单例
  2. bean标签有了属性scope=“singleton”,工厂被加载以后,也就是
 ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
  1. 该行代码运行,工厂会创建,创建之后会将spring配置文件也就是(“/spring-context.xml”)配置文件中的所有对象都创建完成,spring-context.xml中我们定义了一个bean,如下
<?xml version="1.0" encoding="utf8" ?>
<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="userService" class="com.lyx.service.impl.UserServiceImpl" scope="singleton">
   
    </bean>
</beans>
  1. 工厂会创建时直接创建该com.lyx.service.impl.UserServiceImpl的对象,以userService名称存放,getBean()调用
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
        UserDaoImpl userService = (UserDaoImpl)context.getBean("userService");
        List<User> list = userService.queryUsers();
        for(User user : list){
        	System.out.println(user);
        }

  1. prototype多例
<?xml version="1.0" encoding="utf8" ?>
<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="userService" class="com.lyx.service.impl.UserServiceImpl" scope="prototype">
   
    </bean>
</beans>
  1. 多列对应懒汉式,在运行
 ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
  1. 该行代码时,工厂不会创建bean对象,只有我们调用
    getBean(“userService”);时才会创建对象,多列的bean,需要JVM垃圾回收时才会销毁,单列的bean随工厂关闭才销毁
context.close();

在这里插入图片描述

1.3.4.2 其他属性

init-method


  1. 在class定义的包.类里面定义的初始化方法myInit()
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public List<User> queryUsers() {
        List<User> users = userDao.queryUsers();
        return users;
    }
    public void myDestroy(){
        System.out.println("销毁方法执行了");
    }
    public void myInit(){
        System.out.println("初始化方法执行了");
    }

}

在bean标签添加init-method属性,值就是刚刚写的myInit()方法

<?xml version="1.0" encoding="utf8" ?>
<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="userService" class="com.lyx.service.impl.UserServiceImpl" scope="prototype" init-method="myInit">
   
    </bean>
</beans>

destroy-method


  1. 在class定义的包.类里面定义销毁方法myDestroy()
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public List<User> queryUsers() {
        List<User> users = userDao.queryUsers();
        return users;
    }
    public void myDestroy(){
        System.out.println("销毁方法执行了");
    }
    public void myInit(){
        System.out.println("初始化方法执行了");
    }

}

在bean标签添加destroy-method属性,值就是刚刚写的myDestroy()方法

<?xml version="1.0" encoding="utf8" ?>
<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="userService" class="com.lyx.service.impl.UserServiceImpl" scope="prototype" init-method="myInit()" destroy-method="myDestroy">
   
    </bean>
</beans>
  1. 后台自动执行销毁方法,要手动.close才能看见销毁方法有没有执行
  2. 上面介绍多列的时候提到过,手动.close执行会销毁方法,需要的是单列模式,如果bean对象是多列,该销毁方法还是看不见
        ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
        Object userDao = context.getBean("userDao");
        ((ClassPathXmlApplicationContext)context).close();//强转为ApplicationContext子类

注解方式:
在这里插入图片描述
在这里插入图片描述
测试:
在这里插入图片描述

2. Spring配置文件

2.1 依赖注入之Set注入


  • 在Spring创建对象的同时,为其属性赋值,我们称为依赖注入
  • 上面讲解反转控制的时候我们在UserServiceImpl中定义了userDao属性的get/set方法,
public class UserServiceImpl implements UserService{

	private UserDao userDao;
    @Override
    public List<User> queryUser() {
    	List<User> list = userDao.queryUser();
        return list;
    }
    public void setUserDao(UserDao userDao){
    	this.userDao = userDao;
    }
}
  • 在调用userServiceImpl的时候我们是getBean返回了userDao,并通过set方法赋值给userServiceImpl的userDao属性,才成功调用
public class Controller{
	public static void main(String[] args){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
        UserDaoImpl userDao = (UserDaoImpl)context.getBean("userDaoImpl");
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao);
        userService.queryPerson();
	}
}
  • 现在来用依赖注入
  • 直接getBean返回userServiceImpl对象
  • 我们在.xml文件中创建userServiceImpl对象的bean同时,为其属性userDao赋值
  • 配置在一个<property>标签中
  • 这就是依赖注入
<?xml version="1.0" encoding="utf8" ?>
<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="userDaoImpl" class="com.lyx.dao.impl.userDaoImpl"></bean>
    <bean id="userServiceImpl" class="com.lyx.service.impl.userServiceImpl">
        <property name="userDao" ref="userDaoImpl"></property>
    </bean>
 </beans>

在这里插入图片描述

  • 以后直接如下代码调用ServiceImpl类的方法
public class Controller{
	public static void main(String[] args){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
        UserServiceImpl userServiceImpl = (UserServiceImpl)context.getBean("userServiceImpl");
        userServiceImpl.queryPerson();
	}
}
  • 对比一下之前的
public class Controller{
	public static void main(String[] args){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
        UserDaoImpl userDao = (UserDaoImpl)context.getBean("userDaoImpl");
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(userDao);
        userService.queryPerson();
	}
}
  • 以上代码在UserServiceImpl类中没有强耦合,我们把它所有要用到的对象,如userDaoImpl,都放到了容器中,spring搭建并管理工厂,工厂加载配置文件.xml文件,生产配置文件中记录的Bean对象

2.1.1 依赖注入之p命名空间注入

  1. 引入p命名空间
    xmlns:p=“http://www.springframework.org/schema/p”

在这里插入图片描述
在这里插入图片描述
p:userDao-ref="userDao"相当于

<property name="userDao" ref="userDao"/>

p:username ="Gavin"相当于

<property name="username" value="Gavin"/>

完整代码如下:

   <?xml version="1.0" encoding="utf8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                           http://www.springframework.org/schema/p
                           http://www.springframework.org/schema/p/spring-p.xsd"
>
    <bean id="userDao" class="com.lyx.dao.impl.userDaoImpl"></bean>
    <bean id="userService" class="com.lyx.service.impl.userServiceImpl" p:userDao-ref="userDao"></bean>

</beans>

2.2 依赖注入之构造注入


和以上差不多,只不过property标签换成了constructor-arg标签 该标签的name值为你当前bean的class值中类的构造方法的参数类型,ref的值就是<bean id=”userDao“ class=“com.lyx.dao.impl.UserDaoImpl”>的id值

   <?xml version="1.0" encoding="utf8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                           http://www.springframework.org/schema/p
                           http://www.springframework.org/schema/p/spring-p.xsd"
>
    <bean id="userDao" class="com.lyx.dao.impl.userDaoImpl"></bean>
    //set注入:2中方式
    //方式1
    <bean id="userService" class="com.lyx.service.impl.userServiceImpl" p:userDao-ref="userDao"></bean>
    //方式2
    <bean id="userServiceImpl" class="com.lyx.service.impl.userServiceImpl">
        <property name="userDao" ref="userDao"></property>
    </bean>
    
    //构造注入
    <bean id="userService" class="com.lyx.service.impl.userServiceImpl">
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
    </bean>

</beans>

对于构造注入,构造方法的形参,无论是String类型还是int、short、long类型,在赋值时都是value=“值”,因此建议,此种情况,需要配合name以及type属性指定一下
type属性是简单类型直接写类型名称,是对象写包名.类名

<bean id="userService" class="com.lyx.service.impl.userServiceImpl">
        <constructor-arg name="userDao" ref="userDao" type="com.lyx.dao.impl.userDaoImpl"></constructor-arg>
    </bean>

再举一个例子:
通过name属性为构造方法的形参赋值
不给name属性也能赋值,但是顺序必须一致
通过index属性也可以赋值

在这里插入图片描述

    <bean id="student" class="com.lyx.entity.Student" destroy-method="myDestroy" init-method="init">
        <property name="stuNo" value="1"/>
        <property name="stuName" value="Gavin"/>
        <property name="stuAge" value="33"/>
        <!--通过name属性为构造方法的形参赋值-->
        <constructor-arg name="stuAge" value="22"></constructor-arg>
        <constructor-arg name="stuNo" value="11"></constructor-arg>
        <constructor-arg name="stuName" value="Merry"></constructor-arg>
        <!--不给name属性也能赋值,但是顺序必须一致-->
        <constructor-arg value="11"></constructor-arg>
        <constructor-arg value="Merry"></constructor-arg>
        <constructor-arg value="22"></constructor-arg>
        <!--通过index属性也可以赋值-->
        <constructor-arg index="0" value="11"></constructor-arg>
        <constructor-arg index="1" value="Merry"></constructor-arg>
        <constructor-arg index="2" value="22"></constructor-arg>

    </bean>

以上构造方法的依赖注入有一种就够了,不然会报错,我为了方便写在一起。

当然了,UserServiceImpl类中要有构造方法

public class UserServiceImpl implements UserService{

	private UserDao userDao;
    @Override
    public List<User> queryUser() {
    	List<User> list = userDao.queryUser();
        return list;
    }
	 public userServiceImpl(userDaoImpl userDao) {
}
    public void setUserDao(UserDao userDao){
    	this.userDao = userDao;
    }
}

2.3 依赖注入之注入数据类型,set注入


  • 以上注入都是把Dao注入给Service
  • 除了对象的引用可以注入,普通数据类型,集合等到可以在容器中注入
  • 比如Person类中的属性如下,有jdk8种数据类型,数组,集合,自建的Address类 ,有构造方法
  • 注入实现如下

在这里插入图片描述
对于set类型、LIst类型、String[]数组类型。

<set> <list> <array>3个标签可以混着用
比如

private String[]  hobbys;

依赖注入如下:

    <!-- 数组-->
    <property name="hobbys">
        <array>
            <value>football</value>
            <value>basketball</value>
        </array>
    </property>

也可以写成

    <!-- 数组-->
    <property name="hobbys">
        <list>
            <value>football</value>
            <value>basketball</value>
        </list>
    </property>

<list>也可以用<array>替换

这种混用的方式(不推荐),最好是Set用<set>,List用<list>,String[]用<array>

2.3.1 Person类

public class Address {
    private int id;
    private String city;
    
    public int getId() {return id; }
    public void setId(int id) {this.id = id;}
    public String getCity() {return city;}
    public void setCity(String city) { this.city = city;}
}

package com.lyx.entity;

import com.sun.xml.internal.ws.wsdl.writer.document.http.Address;

import javax.swing.*;
import javax.xml.ws.soap.Addressing;
import java.util.*;

public class Person {
    //基本数据类型
    private int id;
    private String name;
    private Date bornDate;
    //数组
    private String[] hobbys;
    //集合
    private Set<String> phones;
    private List<String> names;
    private Map<String,String> contries;
    //文件
    private Properties files;
    //自建类型
    private Address address;

    public int getId() { return id; }

    public void setId(int id) {this.id = id;}

    public String getName() {return name;}

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

    public Date getBornDate() {return bornDate;}

    public void setBornDate(Date bornDate) {this.bornDate = bornDate;}

    public String[] getHobbys() {return hobbys;}

    public void setHobbys(String[] hobbys) {this.hobbys = hobbys;}

    public Set<String> getPhones() {return phones;}

    public void setPhones(Set<String> phones) {this.phones = phones;}

    public List<String> getNames() {return names; }

    public void setNames(List<String> names) {this.names = names;}

    public Map<String, String> getContries() { return contries;}

    public void setContries(Map<String, String> contries) {this.contries = contries;}

    public Properties getFiles() {return files;}

    public void setFiles(Properties files) {this.files = files;}

    public Address getAddress() {return address;}

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

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", bornDate=" + bornDate +
                ", hobbys=" + Arrays.toString(hobbys) +
                ", phones=" + phones +
                ", names=" + names +
                ", contries=" + contries +
                ", files=" + files +
                ", address=" + address +
                '}';
    }
}

2.3.2 person-context.xml配置文件

<?xml version="1.0" encoding="utf8" ?>
<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="address" class="com.lyx.entity.Address">
        <property name="id" value="1"></property>
        <property name="city" value="China"></property>
    </bean>
	<bean name="person" class="com.lyx.entity.Person">
<!--    set注入-->
    <property name="id" value="1"></property>
    <property name="name" value="liuyuxin"></property>
    <property name="bornDate" value="2020/12/12 12:20:30"></property>
    <!-- 数组-->
    <property name="hobbys">
        <array>
            <value>football</value>
            <value>basketball</value>
        </array>
    </property>
    <!-- 集合-->
    <property name="phones">
        <set>
            <value>111</value>
            <value>222</value>
        </set>
    </property>
    <property name="names">
        <list>
            <value>tom</value>
            <value>jack</value>
        </list>
    </property>
    <property name="contries">
        <map>
            <entry key="zg" value="china"></entry>
            <entry key="els" value="russia"></entry>
        </map>
    </property>
    <property name="files">
        <props>
            <prop key="url">jdbc:mysql:xxx</prop>
            <prop key="username">root</prop>
        </props>
    </property>
    <!-- 对象-->
    <property name="address" ref="address"></property>
	</bean>
</beans>

2.4 引入其他配置文件


在这里插入图片描述

<?xml version="1.0" encoding="UTF8" ?>
<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"
>
    <import resource="applicationContext-product.xml"/>
    <import resource="applicationContext-user.xml"/>
</beans>

2.5 特殊值的注入问题

2.5.1 大于号小于号

<value></value>里面要添加<小于号,>大于号,可以用<![CDATA[]]>来写:

<value>  我认为:<![CDATA[ 5>2 ]]>是真命题 </value>

<propery value="">的value里面要添加大于号小于号,只能用&lt;&gt;

2.5.2 空值

如果value值为null,不能写value=“null"

<property value="null"/>

正确写法:

<property>
	<null/>
</property>

当然空值也可以是”“空字符串
直接什么都不写就可以了

//第一种方式
<property value=""/>
//第二种方式
<property>
	<value></value>
</property>

2.6 各种类型的自动装配

在这里插入图片描述

  1. Student 类里面包含对象类型的属性
    在这里插入图片描述
  2. 在配置文件application-Context.xml中

在这里插入图片描述

2.6.1 自动装配autowire="byName"

在这里插入图片描述
byName的意思是bean的id于类的属性名一致;
注意:只有对象类型可以,简单类型不能自动装配

2.6.2 自动装配autowire="byType"

在这里插入图片描述
bean的类型(class)与属性类型一致,
但是这种方式,如果出现2个类型一样的bean,则会报错

2.6.3 自动装配autowire="constructor"

在这里插入图片描述

该Student类的构造方法里面需要JavaCourse类型的形参
配置如下
在这里插入图片描述
如下:
在这里插入图片描述

2.6.4 一次性设置全部的bean自动装配

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns="http://www.springframework.org/schema/beans"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/p
                           http://www.springframework.org/schema/p/spring-p.xsd"
       default-autowire="byName"                    
>

3. Spring配置数据源


  • 连接池可以用C3P0、我用的是Druid

jdbc.properties

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3308/companydb?useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
jdbc.init=1
jdbc.minIdle=1
jdbc.maxActive=20
<?xml version="1.0" encoding="utf8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
                           http://www.springframework.org/schema/p
                           http://www.springframework.org/schema/p/spring-p.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd "
>
    <!--    placeholder占位符-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClass}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="initialSize" value="${jdbc.init}"/>
        <property name="maxWait" value="60000"/>
        <property name="timeBetweenEvictionRunsMillis" value="50000"/>
        <property name="minEvictableIdleTimeMillis" value="3000"/>
    </bean>
   
</beans>

4. LOC使用注解开发


4.1 原始注解

注解开发主要是代替<bean>的配置,对于我们注解的类或者属性,.xml文件配置一个组件扫描,spring把注解了的类自动放入容器中


注解说明
@Component
@Autowired和@Qualifier(”“)
@Repository
@Value
@Scope(“”)
@PostConstruct
@PreDestroy

4.1.1注解说明

Dao和Service的bean配置原始代码如下

    <bean id="userDaoImpl" class="com.lyx.dao.impl.userDaoImpl"></bean>
    <bean id="userServiceImpl" class="com.lyx.service.impl.userServiceImpl">
        <property name="userDao" ref="userDaoImpl"></property>
    </bean>
  • 对于上面DaoImpl的配置,用注解代替,直接到bean的class的包的类下面,也就是这个userDaoImpl类,添加一个@Component(”“),里面的参数就是上面bean的id

  • ServiceImpl的配置,同上,但因为有依赖注入,我们还要用@Autowired和@Qualifier(”“)@autowire与上面的自动装配一样,只添加@Autowired,默认是byType,要想byName,需要添加@Qualifier(“xx”), Qualifier里面参数是上面ref的值,也就是bean的id。
    添加了@Autowired,spring会从Ioc容器中 找一个类型为StudentMapper的bean
    在这里插入图片描述

  • 注解加完了,在MyConfiguration配置类中

package com.lyx.annotation;

import com.alibaba.druid.pool.DruidDataSource;
import com.lyx.entity.Classroom;
import com.lyx.entity.Student;
import com.lyx.mapper.StudentMapper;
import com.lyx.service.StudentService;
import com.lyx.service.impl.StudentServiceImpl;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Controller;

import javax.sql.DataSource;

@Configuration
@ComponentScan(basePackages = "com.lyx")
@PropertySource("classpath:database.properties")
public class MyConfiguration {


      @Value("${jdbc.driver}")
      private String driver;
      @Value("${jdbc.url}")
      private String url;
      @Value("${jdbc.username}")
      private String username;
      @Value("${jdbc.password}")
      private String password;


      @Bean
      public DataSource dataSource(){
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName(driver);
            ds.setUrl(url);
            ds.setUsername(username);
            ds.setPassword(password);
            return ds;
      }

      @Bean
      public SqlSessionFactoryBean sqlSessionFactoryBean(@Autowired DataSource dataSource){
            SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
            sessionFactoryBean.setTypeAliasesPackage("com.lyx.entity");
            sessionFactoryBean.setDataSource(dataSource);
            return sessionFactoryBean;
      }
      @Bean
      public MapperScannerConfigurer mapperScannerConfigurer(){
            MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
            mapperScannerConfigurer.setBasePackage("com.lyx.mapper");
            return mapperScannerConfigurer;
      }
}

测试一下@Autowires注解是否成功找到StudentMapper类型的Bean
在这里插入图片描述

其他注解如下


  1. 对于以上的@Component,不方便区分dao层还是Service层亦或者是Controller层
  2. 所以对于本来要使用@Component的位置,我们在Dao层用@Repository,在Service层用@Service,在Controller层用@Controller
  3. 对于上面使用的@Autowired和@Qualifier(" ")我们可以合并成一个@Resource(name= " ")
  4. @Value注入普通属性,可以通过@Value(”${ jdbc.driver}“)获取jdbc.properties文件的值,赋值给添加这个注解的变量,jdbc.properties必须在.xml配置文件中<context:property-placeholder location=“classpath:jdbc.properties”/>被放入容器中
@Vaule("${jdbc.driver}")
private String driver;//driver=
  1. @Scope(“”)标志Bean的作用范围,单例、多例,值为prototype和singleton在这里插入图片描述

  2. @PostConstruct,使用在方法上,标注该方法是Bean的初始化方法,类似于init-method

  3. @PreDestroy,使用在方法上,标注该方法是Bean的摧毁方法,类似于destroy-method

4.1.2 Bean的生命周期详解:初始化,创建,销毁

复制上面提到的bean标签的其他属性

init-method


  1. 在class定义的包.类里面定义的初始化方法myInit()
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public List<User> queryUsers() {
        List<User> users = userDao.queryUsers();
        return users;
    }
    public void myDestroy(){
        System.out.println("销毁方法执行了");
    }
    public void myInit(){
        System.out.println("初始化方法执行了");
    }

}

在bean标签添加init-method属性,值就是刚刚写的myInit()方法

<?xml version="1.0" encoding="utf8" ?>
<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="userService" class="com.lyx.service.impl.UserServiceImpl" scope="prototype" init-method="myInit">
   
    </bean>
</beans>

destroy-method


  1. 在class定义的包.类里面定义销毁方法myDestroy()
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public List<User> queryUsers() {
        List<User> users = userDao.queryUsers();
        return users;
    }
    public void myDestroy(){
        System.out.println("销毁方法执行了");
    }
    public void myInit(){
        System.out.println("初始化方法执行了");
    }

}

在bean标签添加destroy-method属性,值就是刚刚写的myDestroy()方法

<?xml version="1.0" encoding="utf8" ?>
<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="userService" class="com.lyx.service.impl.UserServiceImpl" scope="prototype" init-method="myInit()" destroy-method="myDestroy">
   
    </bean>
</beans>
  1. 后台自动执行销毁方法,要手动.close才能看见销毁方法有没有执行
  2. 上面介绍多列的时候提到过,手动.close执行会销毁方法,需要的是单列模式,如果bean对象是多列,该销毁方法还是看不见
        ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
        Object userDao = context.getBean("userDao");
        ((ClassPathXmlApplicationContext)context).close();//强转为ApplicationContext子类

注解方式:
在这里插入图片描述
在这里插入图片描述
测试:
在这里插入图片描述

2. 通过注解@Postconstruct @PreDestroy实现初始化和销毁方法

将相应组件加入@Component注解、给初始化方法加@PostConstruct、给销毁方法加@PreDestroy

@PostConstruct:相当于方法一的init
@PreDestroy:相当于方法一的destroy

如果要获取@Component注解中的bean,那么该Bean的名字就是@Component (value=" xxx")的value值xxx

例如如下配置:
在这里插入图片描述
获取该类中的myConverter()方法

MyConverter myConverter = (MyConverter)context.getBean("myConverter");
myConverter.myConverter();
3. 通过实现接口InitializingBean,DisposableBean实现初始化和销毁方法
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component(value = "myFun")
public class MyFunction implements InitializingBean, DisposableBean {
    @Override
    public void destroy() throws Exception {
        System.out.println("实现DisposableBean接口" +
                "的销毁方法...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("实现InitializingBean接口" +
                "的初始化方法...");
    }
}

在这里插入图片描述
测试一下:
在这里插入图片描述

4. 通过实现接口BeanPostProcessor

刚刚是实现2个接口,这里只实现一个接口BeanPostProcessor

  1. 自定义类MyXxx实现接口BeanPostProcessor
    在这里插入图片描述
@Component(value = "myX")
public class MyXxx implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化....");
        return null;
    }
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("销毁....");
        return null;
    }
}

测试运行,发先打印了很多遍初始和销毁:BeanProocessor接口会拦截所有的Bean,给容器中所有的Bean加初始化、销毁方法
在这里插入图片描述
当拦截到Student类的bean时,给自动创建的Student对象添加属性:
在这里插入图片描述

import com.lyx.entity.Student;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component(value = "myX")
public class MyXxx implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化:"+beanName+":"+bean);
        if(bean instanceof Student){
            Student student = (Student) bean;
            student.setStuId(2);
            student.setName("zs123456");
            student.setGrade("g2");
            student.setClassId(1);
            return student;
        }
        return bean;
    }
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("销毁....");
        return bean;
    }
}

测试一下:
在这里插入图片描述


4.2 新注解


以上原始注解,不能全部替代xml配置文件的Bean在例如连接池这种Bean中,我们不能到它的包.类去添加注解,包括组件扫描,也扫描不了

注解说明
@Configuration用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解
@ComponentScan告知spring,哪些包中,有被注解的类、方法、属性,作用和Spring的xml配置文件中的<context:component-scan base-package=“com.lyx”/>一样
@Bean使用在方法上,标注将该方法的返回值存储到Spring容器中 ,如@Bean(“dataSource”),Spring会将当前方法的返回值以指定名称(dataSource)存储到Spring容器中
@PropertySource用来加载.properties文件中的配置,作用和Spring的xml配置文件中的 <context:property-placeholder location=“classpath:jdbc.properties”/>一样
@Import用于导入其他配置文件,作用和Spring的xml配置文件中的<import resource=“classpath:spring.xml”/>一样,@Import({XXX.class,XXX.xml})可以导入多个配置文件

使用步骤看这篇文章:spring注解开发

5. AOP概念

先来看一下
在这里插入图片描述
在这里插入图片描述
切入点:就是可以切入login()的方法(如:showInfo()方法)
切面:就是当前要切入的login()方法,它可以横切到多个业务之中

AOP相关的名词

在这里插入图片描述
在这里插入图片描述


5.1 环境搭建

  1. 导入依赖
<!--        https://mvnrepository.com/artifact/org.springframework/spring-aspects &ndash;&gt;-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

在这里插入图片描述

  1. spring-context.xml引入aop命名空间

在这里插入图片描述

  1. 定义一个额外功能:通知类
    添加到UserServieImpl类的queryUser方法中,在执行该方法内部代码前调用
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("在方法执行前调用");
    }
}
  1. 到spring-context.xml文件中声明
<?xml version="1.0" encoding="utf8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx"

>
	<!--添加额外功能的目标:原始业务-->
    <bean id="userService" class="com.lyx.service.impl.service.userServiceImpl"></bean>
    <!--额外功能:-->
    <bean id="before" class="com.lyx.advice.MyBeforeAdvice"></bean>
    <!--定义切入点-->
    <aop:config>
        <aop:pointcut id="pc_service" expression="execution(* queryUser())"/>
        <aop:advisor advice-ref="before" pointcut-ref="pc_service"/>
    </aop:config>
</beans>

在这里插入图片描述

5.调用如下

 public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
        //context.getBean()动态生成代理类,类型是目标接口
        UserService bean = (UserService)context.getBean("userService");
       //UserServiceImpl bean = (UserServiceImpl) context.getBean("userService");
       //UserServiceImpl bena = context.getBean(UserServiceImpl.class)
       //UserService bean = context.getBean(UserService.class);
        List<User> users = bean.queryUsers();
        for (User user : users) {
            System.out.println(user);
        }
    

运行结果

User{id=1, username='lyx', password='11', gender=1, registTime=Thu Jan 01 00:00:00 CST 1970}
User{id=2, username='nihaoy', password='1244', gender=1, registTime=Sat Apr 30 19:40:43 CST 2022}
User{id=7, username='yusan', password='123456', gender=1, registTime=Sat Apr 23 16:12:19 CST 2022}

5.2 通知类


5.2.1 前置通知类

在这里插入图片描述

5.2.2 后置通知类

在这里插入图片描述

public class MyAfterAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("在方法执行后调用:\n" +
                "调用方法的返回值:" +o+
                ",目标对象:"+o1+
                ",\n调用的方法名:"+method.getName()+
                ",调用方法的参数个数:"+objects.length);
    }
}

在这里插入图片描述
放到spring中
在这里插入图片描述

public static void main(String[] args) {
   ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
   StudentServiceImpl studentService = (StudentServiceImpl) context.getBean("studentService");
   studentService.showStudent();
}

在这里插入图片描述

5.2.3 异常通知类

在这里插入图片描述
在这里插入图片描述

  • args是Object[]类型,所以如果想打印参数列表可以:Arrays.toString(args),我这里打印长度.length就可以了

为目标类写一个会报错的代码:
在这里插入图片描述


在配置文件中声明:
在这里插入图片描述

在这里插入图片描述


运行结果:
在这里插入图片描述

5.2.4 环绕通知类

在这里插入图片描述
在这里插入图片描述
放行方法proceed()的返回值就是插入环绕通知的方法:目标方法showStudens()的返回值,我写的这个方法没有返回值,void
在这里插入图片描述
声明:

在这里插入图片描述
运行结果:
在这里插入图片描述


获取当前目标方法:
在这里插入图片描述
代码如下:

在这里插入图片描述

运行结果:
在这里插入图片描述

总代码,我放一起了,用到时候分开写

package com.lyx.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.ThrowsAdvice;

import java.lang.reflect.Method;

public class MyBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("在方法执行前调用");
    }
}
//后置通知,有异常不执行,方法会因异常而结束,无返回值
class MyAfterReturningAdvice implements AfterReturningAdvice{

    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("在方法执行后调用");
    }
}
//异常通知,有异常才会执行
class MyThrowsAdvice implements ThrowsAdvice{
    public void afterThrowing(Exception ex){
        System.out.println("有异常!!");
    }
}
//环绕通知
class MyMethodInterceptor implements MethodInterceptor{

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("事务开始");
        Object object = methodInvocation.proceed();
        System.out.println("事务结束");
        return object;
    }
}

5.2.5 spring-context.xml文件

<?xml version="1.0" encoding="utf8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx"

>
	 <!--目标:原始业务-->
    <bean id="userService" class="com.lyx.service.impl.service.UserServiceImpl"></bean>
    <!--通知:额外功能-->
    <bean id="before" class="com.lyx.advice.MyBeforeAdvice"></bean>
    <bean id="after" class="com.lyx.advice.MyAfterReturningAdvice"></bean>
    <bean id="throws" class="com.lyx.advice.MyThrowsAdvice"></bean>
    <bean id="mi" class="com.lyx.advice.MyMethodInterceptor"></bean>
    <!--定义切入点-->
    <aop:config>
        <aop:pointcut id="pc_service" expression="execution(* queryUser())"/>
        <aop:pointcut id="pc_service2" expression="execution(* queryUser())"/>
        <aop:pointcut id="pc_service3" expression="execution(* queryUser())"/>
        <aop:pointcut id="pc_service4" expression="execution(* queryUser())"/>
        <aop:advisor advice-ref="before" pointcut-ref="pc_service"/>
        <aop:advisor advice-ref="after" pointcut-ref="pc_service2"/>
        <aop:advisor advice-ref="throws" pointcut-ref="pc_service3"/>
        <aop:advisor advice-ref="mi" pointcut-ref="pc_service4"/>
        
    </aop:config>
</beans>

运行结果如下

在这里插入图片描述

5.3 通配切入点


  • execution内参数依次是修饰符、返回值、包.类、方法名、参数

5.3.1 匹配参数

* *(com.lyx.eneity.User)
匹配的是修饰符、返回值、包.类、方法名都随便用*代替,方法里面的参数必须是User类型的

在这里插入图片描述
如果想对多个方法前插入相同的方法,可以写在一起
在这里插入图片描述

5.3.2 匹配方法名(无参)

* queryUser()
匹配的是修饰符、返回值、包.类随便用*代替,方法名必须是queryUser,()括号里面不写,匹配的是无参的方法,如果要指定参数,参考上一个匹配参数

<aop:pointcut id="pc_service" expression="execution(* queryUser())"/>

5.3.3 匹配方法名(任意参数)

* queryUser(…)
匹配的是修饰符、返回值、包.类随便用*代替,方法名必须是queryUser,(…)括号里面的“…”,匹配的是任意参数

<aop:pointcut id="pc_service" expression="execution(* queryUser(..))"/>

5.3.4 匹配返回值类型

com.lyx.en.User * (…)
匹配的是修饰符随便、返回值必须是User类型、包.类和方法名随便用*代替、(…)括号里面的“…”,匹配的是任意参数

<aop:pointcut id="pc_service" expression="execution(com.lyx.en.User *(..))"/>

5.3.5 匹配类名

* com.lyx.en.User. * (…)
匹配的是修饰符随便、返回值随便、包.类必须是com.lyx.en.User、和方法名随便用*代替、(…)括号里面的“…”,匹配的是任意参数

<aop:pointcut id="pc_service" expression="execution(* com.lyx.en.User.*(..))"/>

5.3.5 匹配包名

* com.lyx.en.User.*.* (..)

匹配的是修饰符随便、返回值随便、包必须是com.lyx.en.User、类随便用*代替、方法名随便用*代替、(…)括号里面的“…”,匹配的是任意参数

上面括号里是两个点,不知道怎么显示出来是三个点

<aop:pointcut id="pc_service" expression="execution(* com.lyx.en.*.*(..))"/>

5.3.6 匹配包名、以及子包名

* com.lyx.en..* .* (..)

匹配的是修饰符随便、返回值随便、包必须是com.lyx.en.User、类随便用*代替、方法名随便用*代替、(…)括号里面的“…”,匹配的是任意参数

<aop:pointcut id="pc_service" expression="execution(* com.lyx.en.*.*(..))"/>

5.3.7 总结

在这里插入图片描述

6. AOP使用注解开发


xml原始代码如下:
在这里插入图片描述
注解方法:

6.1 @Aspect注解使用步骤

在这里插入图片描述

6.1.1 将LogAspectAnnotation类定义为通知类

在这里插入图片描述

6.1.2 配置文件中添加扫描器、开启注解对aop的支持

applicationContext.xml文件中添加
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意

运行发现报错了:
在这里插入图片描述
添加 proxy-target-class=“true” 属性,表示使用CGLIB动态代理织入增强
在这里插入图片描述

		碰到的问题是

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

	 两者的区别:

JDK动态代理实现了接口的类或者直接代理接口,而CGLIB可以代理未实现任何接口的类。另外CGLIB是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理被final修饰的类和方法。

	来看我的通知类LogAspectAnnotation:

在这里插入图片描述
没有实现任何接口,所以要把proxy-target-class=“true”

	再次运行

在这里插入图片描述

6.1.3 @Aspect注解与扫描器无关

@Aspect不需要加入扫描器,只需要开启即可;

在这里插入图片描述

    <!--只针对@Aspect注解-->
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"></aop:aspectj-autoproxy>

    <!--只针对@Component @Repository @Service @Controller修饰的类产生的对象,将其增加到IOC容器中-->
<context:component-scan base-package="com.lyx"></context:component-scan>

6.1.4 完整代码

在这里插入图片描述

例题:

例1:
package com.lyx.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.io.ObjectInputStream;
@Aspect //声明此类是一个切面类:会包含切入点pointcut和通知advice
@Component //声明组件,进入工厂
public class MyAspect {
    //定义切入点
    @Pointcut("execution(* com.lyx.service.UserServiceImpl.*(..))")
    public void pc(){};

    @Before("pc()")
    public void mybefore(JoinPoint a){
        System.out.println("target:"+a.getTarget());
        System.out.println("args:"+a.getArgs());
        System.out.println("method's name:"+a.getSignature().getName());
        System.out.println("before........");
    }

    @AfterReturning(value = "pc()",returning = "ret")
    public void myAfterReturning(JoinPoint a,Object ret){
        System.out.println("after......:"+ret);
    }

    @AfterThrowing(value = "pc()",throwing = "ex")
    public void myThrows(JoinPoint jp,Exception ex){
        System.out.println("throws");
        System.out.println("=="+ex.getMessage());
    }

    @Around("pc()")
    public Object myInterceptor(ProceedingJoinPoint p) throws Throwable{
        System.out.println("interceptor1....");
        Object ret = p.proceed();
        System.out.println("interceptor2....");
        return ret;
    }
}

例2
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component("logAspectAnnotation")//将LogAspectAnnotation放到SpringIOC容器中
@Aspect //此类是一个通知类
public class LogAspectAnnotation {
    @Pointcut("execution(* com.lyx.service.impl.StudentServiceImpl.showStudent())")
    public void pc(){}

    @Before("pc()")
    public void myBefore(){
        System.out.println("注解形式的【前置通知】...");
    }
    @AfterReturning("pc()")
    public void myAfter(){
        System.out.println("注解形式的【后置通知】...");
    }

}

6.2 JointPoint对象

通过注解形式实现的aop,如果想要获取目标对象的一些参数,
则需要使用一个对象:JointPoint

代码如下:给前置后置通知myBefore()和myAfter()方法形参JoinPoint joinPoint
在这里插入图片描述

通过JointPoint对象获取目标方法

package com.lyx.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component("logAspectAnnotation")//将LogAspectAnnotation放到SpringIOC容器中
@Aspect //此类是一个通知类
public class LogAspectAnnotation {
    @Pointcut("execution(* com.lyx.service.impl.StudentServiceImpl.showStudent())")
    public void pc(){}

    @Before("pc()")
    public void myBefore(JoinPoint joinPoint){
        System.out.println("注解形式的【前置通知】...");
        System.out.println("目标对象:"+joinPoint.getTarget()+
                            ",\n方法名:"+joinPoint.getSignature().getName()+
                            ",\n参数列表:"+ Arrays.toString(joinPoint.getArgs()));
    }
    @AfterReturning("pc()")
    public void myAfter(JoinPoint joinPoint){
        System.out.println("注解形式的【后置通知】...");
        System.out.println("目标对象:"+joinPoint.getTarget()+
                ",\n方法名:"+joinPoint.getSignature().getName()+
                ",\n参数列表:"+ Arrays.toString(joinPoint.getArgs()));
    }

}

运行结果:

在这里插入图片描述

6.3 注解形式目标方法的返回值

声明返回值的参数名

在这里插入图片描述
来看以下@AfterReturning的参数
在这里插入图片描述

  @AfterReturning(pointcut = "pc()",returning="returningValue",argNames = "joinPoint,returningValue",value = "")
    public void myAfter(JoinPoint joinPoint,Object returningValue){
        System.out.println("注解形式的【后置通知】...");
        System.out.println("目标对象:"+joinPoint.getTarget()+
                ",\n方法名:"+joinPoint.getSignature().getName()+
                ",\n参数列表:"+ Arrays.toString(joinPoint.getArgs())+
                ",\n方法的返回值:"+returningValue);
    }

运行结果:
在这里插入图片描述

6.4 注解形式的环绕通知:参数类型ProceedingJointPoint

在这里插入图片描述
在这里插入图片描述
代码如下:

@Component("logAspectAnnotation")//将LogAspectAnnotation放到SpringIOC容器中
@Aspect //此类是一个通知类
public class LogAspectAnnotation {
    @Pointcut("execution(* com.lyx.service.impl.StudentServiceImpl.showStudent())")
    public void pc(){}
    
    @Around("pc()")
    public void myAround(ProceedingJoinPoint joinPoint){
        //方法之前:前置通知
        System.out.println("注解形式的环绕方法的前置通知");
        try {
            
            joinPoint.proceed();//执行方法
            System.out.println("注解形式的环绕方法的后置通知");

        } catch (Throwable throwable) {
            System.out.println("注解形式的环绕方法的异常通知");
        } finally {
            System.out.println("注解形式的环绕方法的最终通知");
        }
    }

}

6.5 注解形式的异常通知和最终通知

@AfterThrowing和@After

package com.lyx.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component("logAspectAnnotation")//将LogAspectAnnotation放到SpringIOC容器中
@Aspect //此类是一个通知类
public class LogAspectAnnotation {
    @Pointcut("execution(* com.lyx.service.impl.StudentServiceImpl.showStudent())")
    public void pc(){}

    //异常通知
    @AfterThrowing("pc()")
    public void myException(JoinPoint joinPoint){
        System.out.println("这是注解形式的异常通知:"+joinPoint.getTarget());
    }
    //最终通知
    @After("pc()")
    public void myAfter(JoinPoint joinPoint){
        System.out.println("这是注解形式的最终通知:"+joinPoint.getTarget());
    }

}

在这里插入图片描述

运行结果:
在这里插入图片描述

如果只捕获特定类型的异常,则可以通过第二个参数实现

演示只捕获空指针异常
在这里插入图片描述
运行结果:
在这里插入图片描述
代码:

@Component("logAspectAnnotation")//将LogAspectAnnotation放到SpringIOC容器中
@Aspect //此类是一个通知类
public class LogAspectAnnotation {
    @Pointcut("execution(* com.lyx.service.impl.StudentServiceImpl.showStudent())")
    public void pc(){}

    //捕获针对类型的异常通知
    @AfterThrowing(pointcut = "pc()",throwing = "e")
    public void myException(JoinPoint joinPoint, NullPointerException e){
        System.out.println("这是注解形式的异常通知:"+joinPoint.getTarget()+
                           e.getMessage());
    }
}

6.6 完整代码

package com.lyx.aop;

import org.apache.ibatis.jdbc.Null;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component("logAspectAnnotation")//将LogAspectAnnotation放到SpringIOC容器中
@Aspect //此类是一个通知类
public class LogAspectAnnotation {
    @Pointcut("execution(* com.lyx.service.impl.StudentServiceImpl.showStudent())")
    public void pc(){}

    @Before("pc()")
    public void myBefore(JoinPoint joinPoint){
        System.out.println("注解形式的【前置通知】...");
        System.out.println("目标对象:"+joinPoint.getTarget()+
                            ",\n方法名:"+joinPoint.getSignature().getName()+
                            ",\n参数列表:"+ Arrays.toString(joinPoint.getArgs()));
    }
    @AfterReturning(pointcut = "pc()",returning="returningValue",argNames = "joinPoint,returningValue",value = "")
    public void myAfter(JoinPoint joinPoint,Object returningValue){
        System.out.println("注解形式的【后置通知】...");
        System.out.println("目标对象:"+joinPoint.getTarget()+
                ",\n方法名:"+joinPoint.getSignature().getName()+
                ",\n参数列表:"+ Arrays.toString(joinPoint.getArgs())+
                ",\n方法的返回值:"+returningValue);
    }
    @Around("pc()")
    public void myAround(ProceedingJoinPoint joinPoint){
        //方法之前:前置通知
        System.out.println("注解形式的环绕方法的前置通知");
        try {

            joinPoint.proceed();//执行方法
            System.out.println("注解形式的环绕方法的后置通知");

        } catch (Throwable throwable) {
            System.out.println("注解形式的环绕方法的异常通知");
        } finally {
            System.out.println("注解形式的环绕方法的最终通知");
        }
    }
    //捕获针对类型的异常通知
    @AfterThrowing(pointcut = "pc()",throwing = "e")
    public void myException(JoinPoint joinPoint, NullPointerException e){
        System.out.println("这是注解形式的异常通知:"+joinPoint.getTarget()+
                           e.getMessage());
    }
    //最终通知
    @After("pc()")
    public void myAfter(JoinPoint joinPoint){
        System.out.println("这是注解形式的最终通知:"+joinPoint.getTarget());
    }

}

7. AOP使用Schema形式开发


7.1 < aop:aspect>标签的使用

< aop:advisor>大多用于事务管理
< aop:aspect>大多用于日志,缓存

  1. 不实现接口了,直接写一个普通类,自定义前置通知和后置通知

在这里插入图片描述

import org.aspectj.lang.JoinPoint;

public class LogSchema {
    //JoinPoint类型只适用于注解和Schema形式,实现接口的通知不能调用
    public void MyAfterReturning(JoinPoint joinPoint,Object returnValue) throws Throwable{
        System.out.println("这是Schema形式的后置通知:\n"
                            +"目标对象:"+joinPoint.getThis()+
                            ",\n目标方法:"+joinPoint.getSignature().getName()+
                            ",\n方法参数个数:"+joinPoint.getArgs().length+
                            ",\n方法的返回值:"+returnValue
        );
    }
    public void MyBefore(){
        System.out.println("这是Schema形式的前置通知...");
    }
}
  1. 配置文件中
	<bean id="logSchema" class="com.lyx.aop.LogSchema"></bean>
	
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="pc" expression="execution(* com.lyx.service.impl.StudentServiceImpl.showStudent())"/>
        <!--Schema方式-->
        <aop:aspect ref="logSchema">
            <!--连接线:连接业务showStudent()  和通知MyBefore()-->
            <aop:before method="MyBefore" pointcut-ref="pc"/>
            <!--连接线:连接业务showStudent()  和通知MyAfterReturning()-->
            <aop:after-returning method="MyAfterReturning" pointcut-ref="pc" returning="returnValue"/>
        </aop:aspect>
    </aop:config>

与我们之前写的相比:
以前用aop:advisor标签
在这里插入图片描述
现在用
在这里插入图片描述
注意怎么对应的 这里使用到aop:before、aop:after-returning标签且在aop:aspect标签内部
在这里插入图片描述
最后注意一点,如果想通过后置通知拿到目标方法的返回值,需要自定义一个参数Object returnValue(名字任意),且要在spring配置文件中声明
在这里插入图片描述

上面写的是前置通知和后置通知,来看看异常通知

在这里插入图片描述
上面提示中其他我们都接触过了,可以看到最后还有一个aop:declare-parents,下面解释一下该标签的用法

7.2 <aop:declare-parents/>增加新的功能

AOP中declare-parents为特定的类增加新的功能

如果有这样一个需求,为一个已知的API添加一个新的功能

由于是已知的API,我们不能修改其类,只能通过外部包装。但是如果通过之前的AOP前置或后置通知,又不太合理,最简单的办法就是实现某个我们自定义的接口,这个接口包含了想要添加的方法。

但是JAVA不是一门动态的语言,无法再编译后动态添加新的功能,这个时候就可以使用 aop:declare-parents 来做了.

目标方法的类和接口

public interface Person {
    public void dance();
}
public class Women implements Person {
    @Override
    public void dance() {
        System.out.println("they call it dance,I call it life");
    }
}

想要添加的新功能的类和接口

public interface WarmUp {
   public void doSports();
}
public class Jogging implements WarmUp {
    @Override
    public void doSports() {
        System.out.println("I go Jogging...");
    }
}

配置applicationContext.xml文件,实现AOP

    <bean id="women" class="com.lyx.declareparents.impl.Women"/>
    <bean id="jogging" class="com.lyx.declareparents.impl.Jogging"/>
    <aop:config proxy-target-class="true">
        <aop:aspect>
            <aop:declare-parents types-matching="com.lyx.declareparents.impl.Women"
                                 implement-interface="com.lyx.declareparents.WarmUp"
                                 default-impl="com.lyx.declareparents.impl.Jogging"/>
        </aop:aspect>
    </aop:config>

在这里插入图片描述
目标是 types-matching的类,spring让它实现 implement-interface所指向的接口, 而具体实现则通过default-impl或default-rel属性来赋予。

测试:

public class test {

    public static void main(String[] args) {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        Women women = (Women) context.getBean("women");
        women.dance();
        //这里注意是强转为目标类的接口WarmUp热身类
        WarmUp women1 = (WarmUp) context.getBean("women");
        women1.doSports();
    }
}

所以从spring容器中拿出来的 Women对象 可以被强转换成 WarmUp类型的对象。Spring帮我们使Women类自动实现了WarmUp接口,如何实现接口,这些都是spring背后做了很多事情。而不是代码显示地继承了它的接口。
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

素心如月桠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值