Spring的简单使用(2)

一:AOP框架:

版本一:业务和切面耦合在一起,没有拆分

public class BookServiceImpl {
public void buy(){
System.out.println("事务开启");
System.out.println("买书业务");
System.out.println("事务关闭");
}
}

版本二:使用子类代理的方式拆分业务和切面

public class BookService {
public void buy(){
System.out.println("买书");
}
}
public class SubBookService extends BookService{
@Override
public void buy() {
System.out.println("事务开启");
super.buy();
System.out.println("事务关闭");
}
}

版本三:使用静态代理拆分业务和切面,业务和业务接口已拆分,此时切面耦合在业务中

public interface Service {
void buy();
}

public class BookServiceImpl implements Service {
public void buy() {
System.out.println("买书");
}
}

public class PenServiceImpl implements Service {
public void buy() {
System.out.println("买笔");
}
}

public class Agent implements Service {
private Service target;

public Agent(Service target) {
this.target=target;
}



public void buy() {
System.out.println("事务开启");
target.buy();
System.out.println("十五关闭");
}
}

版本四:使用静态代理拆分业务和业务接口,切面和切面接口

业务接口:

public interface Service {
void buy();
}

业务:

public class BookServiceImpl implements Service {
public void buy() {
System.out.println("买书");
}
}
public class PenServiceImpl implements Service {
public void buy() {
System.out.println("买笔");
}
}

切面接口:

public interface Aop {
default void before() {

}

default void after() {

}


}

切面:

public class LogAop implements Aop{
@Override
public void before() {
System.out.println("在方法前使用日志");
}
}
public class TranstionAop implements Aop{
@Override
public void before() {
System.out.println("事务提交前");
}

@Override
public void after() {
System.out.println("事务提交后");
}
}
代理:
public class Agent implements Service,Aop {
private Service target;
private Aop aop;

public Agent(Service target,Aop aop) {
this.target=target;
this.aop=aop;
}

public void buy() {
aop.before();
target.buy();
aop.after();
}
}

版本五:使用动态代理完成版本四的优化

优化代理:使用动态代理:

public class Agent {
public static Object getAgent(Service target,Aop aop){
return Proxy.newProxyInstance(
//类加载器
target.getClass().getClassLoader(),
//目标对象实现的所有接口
target.getClass().getInterfaces(),
//代理功能实现
new InvocationHandler() {
@Override
public Object invoke(
//生成的代理对象
Object proxy,
//被调用的方法
Method method,
//目标方法的参数
Object[] args) throws Throwable {
//切面
aop.before();
//业务
Object invoke = method.invoke(target, args);
//切面
aop.after();
//切面
return invoke;
}
});
}
}

Aop常用的术语:

 

 

三:AspectJ框架:是一个面向切面的框架,扩展了java语言,提供了强大的切面实现,要比spring原生的aop框架好用的多

有常见的四种类型:前置通知@Before,后置通知:@AfterReturning,环绕通知:@Around,最终通知:@After,定义切入点:@Pointcut(了解)

 

1: AspectJ的前置通知@Before

在目标方法执行前切入切面功能,在切面方法中不可以获得目标方法的返回值,只能得到目标方法的签名

步骤:1创建业务接口2创建业务实现3创建切面类4在applicationContext.xml文件中进行切面绑定

导入的依赖:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>

 业务接口:

public interface BookService {
String buy(String name,String adress);
String show(String name,int age);
}

业务实现:

@Component
public class BookServiceImpl implements BookService{

@Override
public String buy(String name, String adress) {
System.out.println("去学校买书");
return "aaaa";
}

@Override
public String show(String name, int age) {
System.out.println("show方法被调用");
return "aaaa";
}
}

切面类:

@Component
@Aspect//让aspect找到这个切面类
public class MyAspect {
// 规范:1访问权限为public 2方法的返回值是void 3方法名称自定义 4 方法没有参数,如果有也只能是JoinPoint类型
//业务方法 String buy(String name,String adress);
@Before(value = "execution( String com.ztb.aop1.BookServiceImpl.*(..))")
public void mybefore(){
System.out.println("切面方法的前置通知功能实现");
}
}

applicationContex.xml:

<context:component-scan base-package="com.ztb.aop1"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

测试:

@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookserviceImpl =(BookService) context.getBean("bookServiceImpl");
bookserviceImpl.show("dd",1);
}

AspectJ切换JDK动态代理和CGlig动态代理:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy> 只能用接口类型
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>  设置为CGLib子类代理,可以用接口和实现类来接

注意:使用接口来接,永远不会出错

前置通知方法JointPoint:

@Before(value = "execution( String com.ztb.aop1.BookServiceImpl.*(..))")
public void mybefore(JoinPoint jp){

System.out.println("切面方法的前置通知功能实现");
System.out.println(Arrays.toString(jp.getArgs()));//得到目标方法的参数
System.out.println(jp.getSignature());//得到目标方法的签名
}

2:后置通知:

@Component
@Aspect
public class MyAspect {
// 规范:1访问权限为public 2方法的返回值是void 3方法名称自定义
// 4 方法有参数,也可以没有参数,如果目标方法没有参数,则可以写午餐的方法,但一般写有参,这样可以无参或有参,这个切面方法的参数就是目标方法的返回值
//业务方法 String buy(String name,String adress);
//returning:指定目标方法的返回值的名称,则名称必须与切面方法的参数名称一致
@AfterReturning(value = "execution(* com.ztb.aop1.*.*(..))",returning = "obj")
public void myafter(Object obj){

System.out.println("切面方法的后置通知功能实现");

后置通知无法改变目标方法返回值例子:

@Component
@Aspect
public class MyAspect {
// 规范:1访问权限为public 2方法的返回值是void 3方法名称自定义
// 4 方法有参数,也可以没有参数,如果目标方法没有参数,则可以写午餐的方法,但一般写有参,这样可以无参或有参,这个切面方法的参数就是目标方法的返回值
//业务方法 String buy(String name,String adress);
//returning:指定目标方法的返回值的名称,则名称必须与切面方法的参数名称一致
@AfterReturning(value = "execution(* com.ztb.aop1.*.*(..))",returning = "obj")
public void myafter(Object obj){

System.out.println("切面方法的后置通知功能实现");
if(obj!=null){
if(obj instanceof String){
obj = obj.toString().toUpperCase();
System.out.println(obj);
}
}

}
}

后置通知无法可以目标方法返回值例子:

@Component
@Aspect
public class MyAspect {
// 规范:1访问权限为public 2方法的返回值是void 3方法名称自定义
// 4 方法有参数,也可以没有参数,如果目标方法没有参数,则可以写午餐的方法,但一般写有参,这样可以无参或有参,这个切面方法的参数就是目标方法的返回值
//业务方法 String buy(String name,String adress);
//returning:指定目标方法的返回值的名称,则名称必须与切面方法的参数名称一致
@AfterReturning(value = "execution(* com.ztb.aop1.*.*(..))",returning = "obj")
public void myafter(Object obj){

System.out.println("切面方法的后置通知功能实现");
if(obj!=null){
if(obj instanceof String){
obj = obj.toString().toUpperCase();
System.out.println(obj);
}
if(obj instanceof Student){
((Student) obj).setName("李四");
System.out.println(((Student) obj).getName());
}
}

}
}

3:环绕通知:

@Component
@Aspect
public class MyAspect {
// 规范:1访问权限为public 2方法有返回值,此返回值就是目标方法的返回值 3方法名称自定义
// 4 方法有参数,此参数就是目标方法
//业务方法 String buy(String name,String adress);
//returning:指定目标方法的返回值的名称,则名称必须与切面方法的参数名称一致
@Around(value = "execution(* com.ztb.aop1.*.*(..))")
public Object myaround(ProceedingJoinPoint joinPoint) throws Throwable {
//前切功能
System.out.println("环绕通知中的前置功能");
//目标方法调用
Object object = joinPoint.proceed(joinPoint.getArgs());
//后切功能
System.out.println("环绕通知中的后置功能");
return object.toString().toUpperCase();//环绕通知可以更改目标方法的返回值


}
}

4:最终通知:

无论目标方法是否正常执行,最终通知的代码都会被执行

 

@After(value = "mycut()")
public void myaround() {

System.out.println("最终通知");

}

@Pointcut(value = "execution(* com.ztb.aop1.*.*(..))")
public void mycut(){}

 

 

可以为一个目标方法添加各种通知:则执行顺序为:

 

 

为切入点表达式起别名:使用pointcut

@After(value = "mycut()")
public void myaround() {

System.out.println("最终通知");

}

@Pointcut(value = "execution(* com.ztb.aop1.*.*(..))")
public void mycut(){}
posted @ 2022-08-07 23:09  Sunward阳  阅读(40)  评论(0)    收藏  举报