引入依赖
<!--引入依赖-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
<!--这是lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!--这是测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.9</version>
</dependency>
</dependencies>
配置文件Springconfig
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
//在这里主要的目的是一个包扫描,这是第一种方法,第二种方法是创建一个配置类进行包扫描
<context:component-scan base-package="com.biyi"/>
</beans>
不使用代理出现的问题
package com.biyi.service.implment;
import com.biyi.dao.Dao;
import com.biyi.service.UserService;
import com.biyi.user.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImplment implements UserService {
@Autowired
private Dao dao;
@Override
public void addUser(User user) {
System.out.println("每个方法都需要写这样相同的代码");
System.out.println("权限校验");
System.out.println("事务管理");
System.out.println("开启事务");
try {
dao.addUser(user);
System.out.println("提交事务");
} catch (Exception e) {
e.printStackTrace();
System.out.println("事务回滚");
}finally{
System.out.println("关闭链接");
}
}
@Override
public void updateUser(User user) {
dao.updateUser(user);
System.out.println("权限校验");
System.out.println("事务管理");
System.out.println("开启事务");
try {
dao.updateUser(user);
System.out.println("提交事务");
} catch (Exception e) {
e.printStackTrace();
System.out.println("事务回滚");
}finally{
System.out.println("关闭链接");
}
}
@Override
public void deleteById(int id) {
dao.deleteById(id);
}
}
测试类
package com.biyi.admintest;
import com.biyi.controller.UserController;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springconfig.xml")
public class AdminTest {
@Autowired
private UserController userController;
public void fun1(){
userController.addUser();
System.out.println();
}
}
动态代理
package com.biyi.action;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Poxy {
public static void main(String[] args) {
//这是商家的类
//同样商家是与代理这进行对接
/*
* newProxyInstance(ClassLoader loader,代理就是地理star类,目标对象的类加载器 相当于new 目标对象
Class<?>[] interfaces,目标对象实现是接口
InvocationHandler h)创建代理对象的回调函数
*
*
* */
//传过来是目标对象是谁,也就是star,真正的目标对象是star,也就是说真正唱歌个或者跳舞的是star
Star star=new Star();
//如何拿到类加载器,那么通过getclass
Class<? extends Star> aClass = star.getClass();
//说明如果在这里不进行强制类型转换是无法调用sing放方法和dance方法,所以需要进行强制类型转化
//我们是不是要创建代理对象,就newProxyInstance这一个方法就搞定了,
//我们要那star的代理对象,star是通过接口进行代理,所以这个地方需要强制类型转换
//这里aClass.getClassLoader(),类加载器,这里是代理star所以是star的类加载器,所以肯定要传过来star对象
//getInterfaces(),目标对象实现的接口
//new InvocationHandler() 创建代理对象的一个回调函数,在这里其实我们不管是接口,我们也直接去new
Skill dailiobj= (Skill) Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), new InvocationHandler() {
//这是匿名内部类,回调函数,在jdk8之后出现了lambad表达式
/*
这是创建代理对象的回调函数 invoke
* proxy:代理对象
* method:代理对象的方法,也就是执行功能的方法
* args:代理对象的方法的参数
* Throwable:异常处理
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("商家与经纪人沟通");
System.out.println("签合同");
System.out.println("给钱");
/*
* Object obj: 目标对象
* Object... args:参数
*
* */
//Object obj =null;
//我们开始执行方法,方法在哪里,在method里面
// 其实这里是目标对象,所以在这里的参数
Object obj=method.invoke(star, args);
System.out.println("关闭链接");
return obj;
}
});
//代理对象创建完成,我们去调用代理对象的方法
//代理对象的方法
//说明如果在这里不进行强制类型转换是无法调用sing放方法和dance方法,
dailiobj.sing();
dailiobj.dance();
}
}
interface Skill{
void sing();
void dance();
}
//目标类
class Star implements Skill{
public void sing(){
System.out.println("唱歌好听");
}
public void dance(){
System.out.println("跳舞");
}
}
Spring cglib代理
Star star=new Star();
Class<? extends Star> aClass = star.getClass();
// 使用cglib进行代理
//其实cglib和jdk比较相似
//使用cglib创建代理对象
//通过这个Enhancer类来去帮助我们创建代理对象
Enhancer enhancer =new Enhancer();
//在newProxyInstance有三个参数,其实enhancer是一样的
enhancer.setClassLoader(aClass.getClassLoader());
enhancer.setInterfaces(aClass.getInterfaces());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("商家与经纪人沟通");
System.out.println("签合同");
System.out.println("给钱")
Object obj=method.invoke(star,objects);
System.out.println("关闭链接");
return obj;
}
});
//这里我们依然去拿代理对象
Skill dailiobj = (Skill) enhancer.create();
System.out.println(dailiobj.getClass());
//代理对象的方法
//说明如果在这里不进行强制类型转换是无法调用sing放方法和dance方法,
dailiobj.sing();
dailiobj.dance();
}
}
interface Skill{
void sing();
void dance();
}
//目标类
class Star implements Skill{
public void sing(){
System.out.println("唱歌好听");
}
public void dance(){
System.out.println("跳舞");
}
}
Spring aop
实现aop
需求实现service逻辑的业务的增强(事务的管理)
其余的代码为三层代码结构的代码
创建切面类
package com.biyi.aspect;
import org.aopalliance.intercept.Invocation;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.Method;
//编写切面类,这个类就是代理类
//当我们编写这个是一个普通类所以我们要实现
//MethodInterceptor这是aop包里面的接口,不要引错了
@Component
public class MyAspect implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation Invocation) throws Throwable {
//在这里编写增强/通知(业务逻辑)
System.out.println("开启事务");
//在这里我们是不是需要写我们的链接点,哪些方法需要增强,其实谁是方法,这里的在这里Invocation.getMethod()执行目标对象的业务功能,在这里增强方法
//通过MethodInvocation就可以拿到对象即可getMethod
//然后我们要进行切入点,在这里我们要拿到可以增强的方法,
Method method = Invocation.getMethod();
//执行方法,之前都是通过method.invoke这个方法执行,但是在aop里面是拿不到这个方法,但是,在aop里面封装了一个proceed这个方法进行
//具体如何拿到需要增强的方法,我们之前都是通过invoke,但是在aop里面我们是拿不到这个方法的,但是底层给我们封装了proceed这个方法
Object obj = Invocation.proceed();
System.out.println("提交事务");
return obj;
}
}
编写aop代理类的代码之后要告诉我对谁增强,那么就用到了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: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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.biyi"/>
<!--配置aop,指定那些类的哪些方法需要被增强
那么这个有规则
id的值是ID的名称,
expression:表达式 1, within 粗粒度 也就是说只能定位到类上 2,excution是相对来说叫细粒度,可以定位到某个方法的
* com.biyi.service.*.*(..)其中*标示所有这句话说明在com.biyi.service包下的所有方法增强
<aop:config proxy-target-class="true">
proxy-target-class="true"jdk代理
proxy-target-class="false"。cglib代理
但是我们一般不写
-->
<aop:config >
<aop:pointcut id="point" expression="execution(* com.biyi.service.*.*(..))"/>
<aop:pointcut id="point" expression="within(com.biyi.service.*)"/><!--关于within的使用由于只能定位到类上所以后面到不能使用-->
<!--增强什么样的逻辑,也就是我们写的切面,切面类放到容器,在这里引用-->
<!--配置通知-->
<aop:advisor advice-ref="myAspect" pointcut-ref="point"/>
</aop:config>
</beans>
测试类
package com.biyi.admintest;
import com.biyi.controller.UserController;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springconfig.xml")
public class AdminTest {
@Autowired
private UserController userController;
public void fun1(){
userController.addUser();
System.out.println();
}
}
其余的代码为三层代码结构
代码的复用性
其余的就是三层代码结构
下面的这是测试类的代码
aop的通知
在上面的情况是都执行了一遍,但是有时候只需要部分执行,
比如
在方法执行之前执行,在方法执行之后执行,有时候在方法前后进行管理,那么这种情况就需要用到通知
但是如果我们都将这种通知,写到代理对象中就会出现代码冗余,耦合性高这种情况
aop的5大通知
前置通知
后置通知
环绕通知
最终通知
异常通知
创建新的代理类
package com.biyi.aspect;
import org.springframework.stereotype.Component;
@Component
public class MySecond {
public void a(){
System.out.println("前置通知before");
}
}
配置类
前置通知
在springconfig文件里面
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.biyi"/>
<!-- <aop:config>
<aop:pointcut id="point" expression="execution(* com.biyi.service.*.*(..))"/>
<aop:pointcut id="point" expression="within(com.biyi.service.*)"/>由于只能定位到类上所以后面到不能使用–>
<aop:advisor advice-ref="myAspect" pointcut-ref="point"/>
</aop:config>-->
<!--Spring的6大通知
前置通知(就是业务执行之前进行使用),比如权限校验
后置通知(就是业务执行之前进行使用),比如日志管理
环绕通知(就是业务执行之前后进行使用)某个方法执行的时间等
最终通知(方法执行结束之后进行使用)
异常通知就是在方法中出现之后有异常出现进行使用
引介通知
-->
<aop:config>
<!--对于service是开启事务-->
<aop:pointcut id="point1" expression="execution(* com.biyi.service.*.*(..))"/>
<!--对于dao是日志管理-->
<aop:pointcut id="point2" expression="execution(* com.biyi.dao.*.*(..))"/>
<!--使用Spring到通知进行增强-->
<aop:aspect ref="mySecond">
<aop:before method="a" pointcut-ref="point1"/>
</aop:aspect>
</aop:config>
</beans>
创建新的代理类
package com.biyi.aspect;
import org.springframework.stereotype.Component;
@Component
public class MySecond {
public void a(){
System.out.println("前置通知before");
}
}
执行结果
后置通知
配置类
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.biyi"/>
<!-- <aop:config>
<!– <aop:pointcut id="point" expression="execution(* com.biyi.service.*.*(..))"/>–>
<aop:pointcut id="point" expression="within(com.biyi.service.*)"/><!–由于只能定位到类上所以后面到不能使用–>
<aop:advisor advice-ref="myAspect" pointcut-ref="point"/>
</aop:config>-->
<!--Spring的6大通知
前置通知(就是业务执行之前进行使用),比如权限校验
后置通知(就是业务执行之前进行使用),比如日志管理
环绕通知(就是业务执行之前后进行使用)某个方法执行的时间等
最终通知(方法执行结束之后进行使用)
异常通知就是在方法中出现之后有异常出现进行使用
引介通知
-->
<aop:config>
<!--对于service是开启事务-->
<aop:pointcut id="point1" expression="execution(* com.biyi.service.*.*(..))"/>
<!--对于dao是日志管理-->
<aop:pointcut id="point2" expression="execution(* com.biyi.dao.*.*(..))"/>
<!--使用Spring到通知进行增强-->
<aop:aspect ref="mySecond">
<aop:before method="a" pointcut-ref="point1"/>
<aop:after method="after" pointcut-ref="point2"/>
</aop:aspect>
</aop:config>
</beans>
代理类
package com.biyi.aspect;
import org.springframework.stereotype.Component;
@Component
public class MySecond {
public void a(){
System.out.println("前置通知before");
}
public void after(){
System.out.println("后置通知after");
}
}
环绕通知和异常通知
配置文件
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.biyi"/>
<!-- <aop:config>
<!– <aop:pointcut id="point" expression="execution(* com.biyi.service.*.*(..))"/>–>
<aop:pointcut id="point" expression="within(com.biyi.service.*)"/><!–由于只能定位到类上所以后面到不能使用–>
<aop:advisor advice-ref="myAspect" pointcut-ref="point"/>
</aop:config>-->
<!--Spring的6大通知
前置通知(就是业务执行之前进行使用),比如权限校验
后置通知(就是业务执行之前进行使用),比如日志管理
环绕通知(就是业务执行之前后进行使用)某个方法执行的时间等
最终通知(方法执行结束之后进行使用)
异常通知就是在方法中出现之后有异常出现进行使用
引介通知
-->
<aop:config>
<!--对于service是开启事务-->
<aop:pointcut id="point1" expression="execution(* com.biyi.service.*.*(..))"/>
<!--对于dao是日志管理-->
<aop:pointcut id="point2" expression="execution(* com.biyi.dao.*.*(..))"/>
<!--使用Spring到通知进行增强-->
<aop:aspect ref="mySecond">
<aop:before method="a" pointcut-ref="point1"/>
<aop:after method="after" pointcut-ref="point2"/>
<aop:around method="around" pointcut-ref="point1"/>
<aop:after-returning method="afterreturning" pointcut-ref="point1"/>
<aop:after-throwing method="afterthrow" pointcut-ref="point1" throwing="e"/>
</aop:aspect>
</aop:config>
</beans>
代理类
package com.biyi.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
@Component
public class MySecond {
public void a(){
System.out.println("前置通知before");
}
public void after(){
System.out.println("后置通知after");
}
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("方法执行前");
Object obj = joinPoint.proceed();
System.out.println("方法执行后");
return obj;
}
public void afterreturning(){
System.out.println("最终通知");
}
public void afterthrow(Exception e){
//注意:这个throw是只有异常才能执行,否则不执行,比如在service里面System.out.println(1/0)执行就会执行这个异常
System.out.println(1/0);
}
}
package com.biyi.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Component;
@Component
public class MySecond {
public void a(){
System.out.println("前置通知before");
}
public void after(){
System.out.println("后置通知after");
}
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("方法执行前");
Object obj = joinPoint.proceed();
System.out.println("方法执行后");
return obj;
}
public void afterreturning(){
System.out.println("最终通知");
}
public void afterthrow(Exception e){
//注意:这个throw是只有异常才能执行,否则不执行,比如在service里面System.out.println(1/0)执行就会执行这个异常
System.out.println("throwing");
}
}
注解通知开发
package com.biyi.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect//标示这个类就是一个切面类
public class MyThird {
@Pointcut("execution(* com.biyi.service.*.*(..))")
public void print1(){}
@Before("print1()")
public void before(){
System.out.println("这是前置通知");
}
@After("print1()")
public void after(){
System.out.println("后置通知after");
}
@Around("print1()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("方法执行前");
Object obj = joinPoint.proceed();
System.out.println("方法执行后");
return obj;
}
@AfterReturning("print1()")
public void afterreturning(){
System.out.println("最终通知");
}
@afterthrowing("print1()")
public void afterthrow(){
//注意:这个throw是只有异常才能执行,否则不执行,比如在service里面System.out.println(1/0)执行就会执行这个异常
System.out.println("throwing");
}
}
在配置类里面添加开启aop注解驱动
<!--开启aop的驱动-->
<aop:aspectj-autoproxy/>