自己学习Spring5整理的笔记
Spring学习过程
IOC部分
1构建一个HelloSpring
1.1构建步骤
在java文件夹中创建Java文件
在resources中创建beans.xml 即容器
在容器中进行配置 在IDEA中可以直接在XML Config下找到Spring Config进行自动配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring来创建对象,在Spring中这些都称为Bean
Bean相当于 对象 = new 类名();
id = 变量名
class = new的对象
property 相当于给对象中的属性设置一个值
-->
<bean id="hello" class="HelloSpring">
<!--hello是一个对象,借助了class,在下面的property中给这个类的变量赋值就行-->
<property name="str" value="Spring"/>
</bean>
</beans>
在mytest中进行使用
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//获取Spring的上下文对象 即容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");//选定容器
//我们的对象现在都在Spring中管理了,我们要使用,就直接去里面取
HelloSpring hello = (HelloSpring) context.getBean("hello");
System.out.println(hello.toString());
}
}
这一整个过程就叫做控制反转:
控制:由Spring来控制创建
反转:程序本身不创建对象,变成被动的接收对象
依赖注入:用set方法进行注入,若删除setStr就会报错
这就是IOC(控制反转)
2 IOC创建对象方式
1.调用了无参构造方法 就可以建立一个普通的对象
2.若定义了有参的构造方法,就需要以下方法去解决
1.下标赋值
<!--1.下标赋值-->
<bean id="User" class="User">
<constructor-arg index="0" value="ZYB"/>
</bean>
2.类型赋值
<!--不建议使用2.赋类型值 若是在基本类型中type就为其类型,引用类型就用java.lang.String-->
<bean class="User" id="User">
<constructor-arg type="java.lang.String" value="zyb">
</constructor-arg>
</bean>
3.直接通过参数名来设置 最常使用
<!--3.直接通过参数名来构造-->
<bean id="User" class="User">
<constructor-arg name="name" value="Zyb"/>
</bean>
需要注意的是,在Spring初始化的时候,就已经把容器初始化了。即无参构造方法都会被调用
3 Spring配置说明
3.1别名
即前面的name可用alias后面的别名替换 但二者均可使用
<alias name = "User" alias = "abcd"/>
3.2 bean的配置
其中name就相当于是别名
class是其全限定名:包名+类型
<bean id = "User" class = "User" name = "uuser,u1,u2,u3">
3.3 import
import一般用于团队开发使用,他可以将多个配置文件合并为一个
下列代码就将beans和bean2合并成一个配置文件
<?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">
<import resource="bean2.xml"/>
</beans>
4 DI 依赖注入
4.1 set注入
依赖:bean对象的创建依赖于容器
注入:bean对象的所有属性,由容器来注入
首先写好实体类
//Address
public class Address {
public String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
//Student
import java.util.*;
public class Student {
private String name;
private Address address;
private String[] book;
private List<String> hobbies;
private Map<String, String> card;
private Set<String> games;
private Properties info;
private String wife;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBook() {
return book;
}
public void setBook(String[] book) {
this.book = book;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public Map<String, String> getCard() {
return card;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address.toString() +
", book=" + Arrays.toString(book) +
", hobbies=" + hobbies +
", card=" + card +
", games=" + games +
", info=" + info +
", wife='" + wife + '\'' +
'}';
}
}
beans.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="Address">
<property name="address" value="绍兴"/>
</bean>
<bean id="student" class="Student">
<!--1.普通值注入,直接使用value-->
<property name="name" value="ZYB"/>
<!--2.bean注入,直接使用ref-->
<property name="address" ref="address"/>
<!--3.数组注入-->
<property name="book">
<array>
<value>红楼梦</value>
<value>水浒传</value>
<value>西游记</value>
<value>三国演义</value>
</array>
</property>
<!--4.List注入-->
<property name="hobbies">
<list>
<value>听歌</value>
<value>敲代码</value>
<value>看电影</value>
</list>
</property>
<!--5.Map注入-->
<property name="card">
<map>
<entry key="身份证" value="111111222222223333"/>
<entry key="银行卡" value="123123123"/>
</map>
</property>
<!--6.set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>COC</value>
<value>BOB</value>
</set>
</property>
<!--7.null值注入-->
<property name="wife">
<null/>
</property>
<!--8.Properties注入-->
<property name="info">
<props>
<prop key="姓名">小明</prop>
<prop key="学号">2012190535</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
</beans>
4.2构造方法注入
1.需要在方法中构建带参构造方法
2.用constructor-arg进行信息输入
4.3 c命名空间和p命名空间注入
使用前都要添加相关的xml文件 需要构造一个实体类
都是可以直接注入属性值的方法
都是用set方法注入
public class User {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
p命名空间注入是通过set方法注入
<bean id="user" class="User" p:name="zyb" p:age="18"/>
c命名空间是通过构造器注入,需要在对应类中构造有参和无参构造方法才可执行
<bean id="user" class="User" c:name="zyb" c:age="18"/>
4.4 内部bean、外部bean和级联赋值
外部bean:直接在beans标签内部直接定义的bean对象,外部bean可以被多个bean对象引用
内部bean:在某个bean标签的内部定义的bean对象,内部bean只能被某个对象的某个属性引用。
实现类代码
package pojo;
import dao.Def;
public class User {
public Def def;
private String uname;
public void setUname(String uname) {
this.uname = uname;
}
public void setDef(Def def) {
this.def = def;
}
public void out(){
System.out.println("My name is" + uname);
}
@Override
public String toString() {
return "User{" +
"def=" + def +
", uname='" + uname + '\'' +
'}';
}
}
package dao;
public class Def {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
public void oout(){
System.out.println("this company is broken");
}
}
外部bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="pojo.User">
<property name="uname" value="ZYB"/>
<property name="def" ref="def"/>
</bean>
<bean id="def" class="dao.Def">
</bean>
</beans>
内部bean
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="user" class="pojo.User">
<property name="uname" value="ZYB"/>
<property name="def">
<bean id="def" class="dao.Def">
</bean>
</property>
</bean>
</beans>
级联赋值
<?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="user" class="pojo.User">
<property name="uname" value="ZYB"/>
<property name="def" ref="def"/>
</bean>
<bean id="def" class="dao.Def">
<property name="dname" value="商务部"/>
</bean>
</beans>
4.5工厂bean
实现类
即虽然我们定义的是User类的bean,但是当User实现了 FactoryBean接口,并且在其中实现getObject方法,就可以在Test中定义getObject方法中返回类型的对象。
package dao;
public class Def {
private String dname;
public void setDname(String dname) {
this.dname = dname;
}
public void oout(){
System.out.println("this company is broken");
}
}
package pojo;
import dao.Def;
import org.springframework.beans.factory.FactoryBean;
public class User implements FactoryBean<Def> {
public Def def;
private String uname;
public void setUname(String uname) {
this.uname = uname;
}
public void setDef(Def def) {
this.def = def;
}
public void out(){
System.out.println("My name is" + uname);
}
@Override
public String toString() {
return "User{" +
"def=" + def +
", uname='" + uname + '\'' +
'}';
}
@Override
public Def getObject() throws Exception {
return def;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return FactoryBean.super.isSingleton();
}
}
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="user" class="pojo.User">
<property name="uname" value="ZYB"/>
<property name="def" ref="def"/>
</bean>
<bean id="def" class="dao.Def">
<property name="dname" value="商务部"/>
</bean>
</beans>
4.6 Bean的作用域
Bean有六种作用域
在这里重点解释singleton和prototype两种
4.6.1 singleton
即单例,我们对于每一个bean,都只能建立一个实例
在Java中,都是隐式的默认单例
package com.example.spring03;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Mytest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
System.out.println(user == user2);
}
}
//结果为true
我们可以在bean的末尾加入scope来将隐式转为显式
<bean id="user" class="com.example.spring03.User" scope="singleton">
4.6.2prototype
即原型模式,每个引用都会指向一个新的对象
需要在末尾改变scope使其变为原型模式
<bean id="user" class="com.example.spring03.User" scope="prototype">
5 Bean的自动装配
自动装配使Spring满足bean依赖的一种方式
Spring会在上下文中自动寻找并自动给bean装配属性
Spring中有三种装配方式
1.在xml中显示的配置
2.在Java中显示配置
3.隐式的自动装配bean
5.1byName
会自动在容器上下文中查找,和自己对象方法后面的值对应的bean id
但一旦id名乱取,就无法使用
<bean id="people" class="pojo.People" autowire="byName">
5.2byType
会自动在容器上下文中查找,和自己对象属性类型相同的bean
但一旦id名的类型(class)一致,就无法使用
但byType甚至可以取消id进行使用
<bean id="people" class="pojo.People" autowire="byType">
5.3 使用注解实现自动装配
5.3.1 什么是注解
1.注解是代码中的特殊标记,格式是@注解名称(属性名称=属性值,属性名称=属性值…)
2.注解作用在类,方法,属性上面
3.使用注解的目的是让xml配置更加简洁,更加优雅
5.3.2 Spring针对Bean管理中创建对象提供注解
@Component
@Service
@Controller
@Repository
*上面四个注解功能是一样的,都可以用来创建bean实例
5.3.3 基于注解方式实现对象创建
第一步 引入aop依赖
第二步 开启组件扫描
引入contex约束 配置注解支持
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
开启组件扫描
<context:component-scan base-package="dao">
</context:component-scan>
<context:component-scan base-package="pojo">
</context:component-scan>
<!--多个包也可写成如下方法-->
<context:component-scan base-package="dao,pojo">
</context:component-scan>
<!--也可写成上层目录-->
创建类,在类上面添加创建对象注解
以@Component(value = “xxx”)为例
其作用相当于自动创建了一个bean
注解中的value可以省略,不写的话就默认为类的名称首字母小写
package service;
import org.springframework.stereotype.Component;
//注解中的value可以省略,不写的话就默认为类的名称首字母小写
@Component(value = "userService") //等价于 <bean id="userService" class="..."/>
public class UserService {
public void add(){
System.out.println("Service add....");
}
}
5.3.4 扫描配置问题
常规扫描,即会扫描该包中所有的注解 若有多个包,就需要用,隔开
<context:component-scan base-package="pojo"/>
非常规扫描 表示只扫描注解类中的Controller
<context:component-scan base-package="pojo" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
非常规扫描 表示不扫描注解中的Controller
<context:component-scan base-package="pojo">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
5.3.5 基于注解方式实现属性注入
@Autowired:根据属性类型进行自动装配
1.把service和dao的对象都进行创建,在service和dao类添加创建对象注解
2.在service中注入dao对象,在service类中添加dao类型属性,在属性上面使用注解
实现类代码:
package dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("dao add");
}
}
package service;
import dao.UserDao;
import dao.UserDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
//注解中的value可以省略,不写的话就默认为类的名称首字母小写
@Service(value = "userService") //等价于 <bean id="userService" class="..."/>
public class UserService {
@Autowired//根据类型进行注入
//不需要添加set方法
private UserDaoImpl userDaoImpl;
public void add(){
System.out.println("Service add....");
userDaoImpl.add();
}
}
测试类代码:
package service;
import dao.UserDao;
import dao.UserDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
//注解中的value可以省略,不写的话就默认为类的名称首字母小写
@Service(value = "userService") //等价于 <bean id="userService" class="..."/>
public class UserService {
@Autowired//根据类型进行注入
//不需要添加set方法
private UserDaoImpl userDaoImpl;
public void add(){
System.out.println("Service add....");
userDaoImpl.add();
}
}
@Qualifier:根据属性名称进行注入
需要和@Autowired一起配合使用
比如,一个接口有多个实现类对象,若用@Autowired来进行注入,就会导致错误,因为是相同类型无法找到对应的实现类,而若用@Qualifier进行注入,则可以通过value注明到底是哪个实现类
@Resource:既可以根据属性类型注入,也可以根据属性名称注入,默认根据属性名称
JDK11之后就不可使用了,是属于JDK的注解而非Spring,因此JDK不支持后就不再可以使用了
以上三者都是对对象进行注入
@Value:注入普通类型
@Value(value = "zyb")
private String name;
@Value(value = "18")
private int old;
5.3.6 完全注解开发
1.创建配置类,替代xml配置文件
package config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import pojo.User;
@Configuration//把当前类作为配置类,替代xml文件
@ComponentScan(basePackages = {"config","dao"})//类似原先xml文件中的scan
public class Myconfig {
@Bean //只写在方法上,返回值是一个对象 类名相当于id,返回值类型相当于class
public User getUser(){
return new User();
}
}
2.编写测试类
package com.example.spring08;
import config.Myconfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import pojo.User;
public class Mytest {
public static void main(String[] args) {
//加载配置类
ApplicationContext context = new AnnotationConfigApplicationContext(Myconfig.class);
//其余步骤与前面都相同
User getUser = (User) context.getBean("getUser");//bean的名字就是方法的名字相当于id
System.out.println(getUser.getName());
}
}
5.3.7 小结
xml用于管理bean
注解只用于属性的注入即可
需要加入配置
AOP部分
1 AOP的概念
1.1面向切面编程(面向方面编程)
可以进一步对业务逻辑进行隔离,从而使得业务逻辑个部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率。即不通过修改源代码就可以产生新的功能。
1.2底层原理
AOP底层使用了动态代理
1.2.1 有接口的情况
JDK中的动态代理
具体过程
1.使用Proxy类里面的方法创建代理对象
2.调用newProxyInstance方法 其中有三个参数
public static Object newProxyInstance
(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
第一个参数 是一个类加载器
第二个参数 增强方法所在的类,这个类实现的接口,支持多个接口
第三个参数 实现这个接口InvocationHandler,创建代理对象,写增强的方法
编写JDK动态代理代码
1.创建接口,定义方法
package com.example.spring08;
public interface UserDao {
public int add(int a, int b);
public String update(String id);
}
2.创建接口的实现类,实现方法
package com.example.spring08;
public class UserDaoImpl implements UserDao{
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public String update(String id) {
return id;
}
}
实现Proxy内的newProxyInstance方法
package com.example.spring08;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class JDKProxy {
public static void main(String[] args) {
//创建接口实现类的代理对象
Class[] interfaces = {UserDao.class};
UserDaoImpl UserDao = new UserDaoImpl();
UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(UserDao));
int result = dao.add(1,2);
System.out.println("result: "+ result);
}
}
class UserDaoProxy implements InvocationHandler{
//1.把创建的是谁的代理对象,把那个谁传递过来
//通过有参构造进行传递
private Object obj;
public UserDaoProxy(Object obj) {
this.obj = obj;
}
//增强的逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法之前
System.out.println("方法之前执行...."+method.getName()+":传递的参数..."+ Arrays.toString(args));
//被增强的方法执行
Object res = method.invoke(obj, args);
//方法之后
System.out.println("方法之后执行..."+obj);
return null;
}
}
1.2.2没有接口的情况
CGLIB的动态代理
1.3 AOP操作术语
1.连接点
类中可以被增强的方法就称为连接点
2.切入点
类中实际被真正增强的方法就称为切入点
3.通知(增强)
实际被增强的逻辑部分称为通知
通知有多种类型
1.前置通知:在方法之前执行
2.后置通知:在方法之后执行
3.环绕通知:在方法的前面和后面都做执行
4.异常通知:方法出现异常时执行
5.最终通知:无论什么情况都会执行 类似finally
4.切面
把通知应用到切入点的过程就称为切面 是一个动作
1.4 准备工作
在Spring框架中,一般基于AspectJ实现AOP操作
1.AspectJ不是Spring的组成部分,是独立的AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
基于AspectJ实现AOP操作
(1)基于xml配置文件实现
(2)基于注解方式实现 (使用较多)
在项目工程中引入AOP相关的依赖
切入点表达式
(1)切入点表达式的作用:知道要对哪个类里面的哪个方法进行增强
(2)语法结构:
execution([权限修饰符] [返回类型] [类的全路径] [方法名称]([参数列表]))
示例1:对com.atguigu.dao.BookDao的add方法进行增强
execution(* com.atguigu.dao.BookDao.add(…) ) *代表任意路径 返回类型忽略 参数列表用…代替
示例2:对com.atguigu.dao.BookDao的所有方法进行增强
execution(* com.atguigu.dao.BookDao.*(…) ) 代表对类中的所有方法进行增强
示例3:对com.atguigu.dao.BookDao的所有类,所有方法进行增强
execution(* com.atguigu.dao..(…) )
2 AspectJ注解
1.创建类,在类里面定义方法
package aopanno;
//被增强的类
public class User {
public void add(){
System.out.println("add......");
}
}
2.创建增强类
1.在增强类里面,创建方法,让不同方法代表不同通知类型
package aopanno;
//增强的类
public class UserProxy {
//做一个前置通知
public void before(){
System.out.println("before.....");
}
}
3.进行通知的配置
1.在spring配置文件中开启注解扫描
<?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"
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="aopanno"></context:component-scan>
</beans>
2.使用注解创建User和UserProxy对象
利用@Component
3.在增强类上面添加注解@Aspect
package aopanno;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
//增强的类
@Component
@Aspect//生成代理对象
public class UserProxy {
//做一个前置通知
public void before(){
System.out.println("before.....");
}
}
4.在Spring的配置文件中开启生成代理对象
<?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"
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="java"></context:component-scan>
<!--开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
4.配置不同类型的通知
在增强类,在作为通知的方法上面添加通知类型注解,使用切入点表达式进行配置
在增强类方法前面使用@Before等
package aopanno;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
//增强的类
@Component
@Aspect//生成代理对象
public class UserProxy {
//@Before注解表示作为前置通知
@Before(value = "execution(* User.add(..))")
//做一个前置通知
public void before(){
System.out.println("before.....");
}
//@AfterReturning表示后置通知
@AfterReturning(value = "execution(* User.add(..))")
public void afterReturning(){
System.out.println("afterReturning.....");
}
//@After注解表示最终通知
@After(value = "execution(* User.add(..))")
public void after(){
System.out.println("after.....");
}
//@AfterThrowing表示异常通知
@AfterThrowing(value = "execution(* User.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing.....");
}
//@Around表示环绕通知
@Around(value = "execution(* User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("被增强之前");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("被增强之后");
}
}
这里需要注意的是,Aspect是在增强类上方,而@Before等则是在方法上面
且环绕异常要特殊对待
上代码执行结果:
被增强之前
before…
add…
afterReturning…
after…
被增强之后
异常之间的关系
环绕前通知在前置通知之前
环绕后通知在最终通知之后
若增强通知未被执行 则不执行后置通知
3 两大特殊情况
1.相同切入点进行抽取
若很多通知是同一个execution,那么可以用如下代码进行操作
//相同切入点抽取
@Pointcut(value = "execution(* User.add(..))")
public void pointdemo(){
}
//@Before注解表示作为前置通知
@Before(value = "pointdemo()")//将execution直接换成pointdemo()
//做一个前置通知
public void before(){
System.out.println("before.....");
}
2.有多个增强类对同一个方法进行增强 可设置增强类的优先级
利用@Order进行设置,内部数字越小,优先级越高,最小为0
@Component
@Aspect//生成代理对象
@Order(1)//数字越小优先级越高
public class UserProxy {
}
1.7AspectJ配置文件
创建增强类和被增强类
package aopxml;
public class Book {
public void buy(){
System.out.println("buy.....");
}
}
package aopxml;
public class BookProxy {
public void before(){
System.out.println("before.....");
}
}
在spring配置文件中创建两个类的对象
<!--对象创建-->
<bean id="book" class="aopxml.Book"></bean>
<bean id="bookProxy" class="aopxml.BookProxy"></bean>
在spring配置文件中配置切入点
<?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"
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--对象创建-->
<bean id="book" class="aopxml.Book"></bean>
<bean id="bookProxy" class="aopxml.BookProxy"></bean>
<!--AOP的增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* aopxml.Book.buy())"/>
<!--配置切面-->
<aop:aspect ref="bookProxy">
<!--增强作用在具体的方法上-->
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
</beans>
4 完全注解开发
@Configuration
@ComponentScan(basePackages = {"xxx"})//相当于扫描
@EnableAspectJAutoProxy(proxyTargetClass = true)//相当于Aspect的开启