Spring运用

1 现有业务层开发存在问题

1.定义业务接口的代码如下:

package com.txw.service;

/**
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public interface UserService {
    
    
    /**
     * 保存
     * @param name
     */
    public void save(String name);

    /**
     * 根据id删除
     * @param id
     */
    public void delete(String id);

    /**
     * 修改
     */
    public void update();

    /**
     * 根据名字查询所有
     * @param name
     * @return
     */
    public String findAll(String name);

    /**
     * 根据id查询一个
     * @param id
     * @return
     */
    public String findOne(String id);
}

如图所示:在这里插入图片描述
2.实现业务接口的代码如下:

package com.txw.service.impl;

import com.txw.service.UserService;
/**
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public class UserServiceImpl implements UserService {
    
    

    /**
     * 保存
     * @param name
     */
    @Override
    public void save(String name) {
    
    
        try {
    
    
            System.out.println("开启事务");
            System.out.println("处理业务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        }catch (Exception e){
    
    
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }

    /**
     * 根据id删除
     * @param id
     */
    @Override
    public void delete(String id) {
    
    
        try {
    
    
            System.out.println("开启事务");
            System.out.println("处理业务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        }catch (Exception e){
    
    
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }

    /**
     * 修改
     */
    @Override
    public void update() {
    
    
        try {
    
    
            System.out.println("开启事务");
            System.out.println("处理业务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        }catch (Exception e){
    
    
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }

    /**
     * 根据名字查询所有
     * @param name
     * @return
     */
    @Override
    public String findAll(String name) {
    
    
        try {
    
    
            System.out.println("开启事务");
            System.out.println("处理业务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        }catch (Exception e){
    
    
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return name;
    }

    /**
     * 根据id查询一个
     * @param id
     * @return
     */
    @Override
    public String findOne(String id) {
    
    
        try {
    
    
            System.out.println("开启事务");
            System.out.println("处理业务逻辑,调用DAO~~~");
            System.out.println("提交事务");
        }catch (Exception e){
    
    
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return id;
    }
}

如图所示:在这里插入图片描述
问题:从上图中可以看出,现有业务层中控制事务代码出现了大量的冗余,如何解决现有业务层出现的冗余问题?
3.编写spring.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="userService" class="com.txw.service.impl.UserServiceImpl"></bean>
</beans>

如图所示:在这里插入图片描述
4.编写UserServiceTest的代码如下:

package com.txw.test;

import com.txw.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * 测试
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public class UserServiceTest {
    
    

    @Test
    public void test(){
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserService) context.getBean("userService");
        userService.save("Adair");
    }
}

如图所示:在这里插入图片描述

2 代理引言

2.1 什么是代理

代理: 指的是java中的一种设计模式

2.2 为什么需要代理

很多时候除了当前类能够提供的功能外,我们还需要补充一些额外功能。

2.3 代理的作用

代理对象可以在客户和目标对象之间起到中介作用,从而为目标对象增添额外的功能

2.4 代理图例

在这里插入图片描述

3 静态代理的开发

目标类|对象(target):被代理类称之为目标类|或者被代理的对象的称之为目标对象。
开发代理的原则: 代理类和目标类功能一致且实现相同的接口,同时代理类中依赖于目标类对象

3.1 开发静态代理类

代码如下:

package com.txw.util;

import com.txw.service.UserService;
/**
 * 静态代理类
 * 开发原则:代理类和目标类实现相同接口,依赖于真正的目标类
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public class UserServiceStaticProxy implements UserService {
    
    

    // 真正的目标类
    // target 原始业务逻辑对象
    private UserService userService;

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

    /**
     * 保存
     * @param name
     */
    @Override
    public void save(String name) {
    
    
        try {
    
    
            System.out.println("开启事务");
            userService.save(name);         // 调用真正业务逻辑方法
            System.out.println("提交事务");
        }catch (Exception e){
    
    
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }

    /**
     * 根据id删除
     * @param id
     */
    @Override
    public void delete(String id) {
    
    
        try {
    
    
            System.out.println("开启事务");
            userService.delete(id);     // 调用真正业务逻辑方法
            System.out.println("提交事务");
        }catch (Exception e){
    
    
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }

    /**
     * 修改
     */
    @Override
    public void update() {
    
    
        try {
    
    
            System.out.println("开启事务");
            userService.update();       // 调用真正业务逻辑方法
            System.out.println("提交事务");
        }catch (Exception e){
    
    
            System.out.println("回滚事务");
            e.printStackTrace();
        }
    }

    /**
     * 根据名字查询所有
     * @param name
     * @return
     */
    @Override
    public String findAll(String name) {
    
    
        try {
    
    
            System.out.println("开启事务");
            String result = userService.findAll(name);      // 调用真正业务逻辑方法
            System.out.println("提交事务");
            return result;
        }catch (Exception e){
    
    
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据id查询一个
     * @param id
     * @return
     */
    @Override
    public String findOne(String id) {
    
    
        try {
    
    
            System.out.println("开启事务");
            // 调用目标类方法
            String one = userService.findOne(id);       // 调用真正业务逻辑方法
            System.out.println("提交事务");
            return one;
        }catch (Exception e){
    
    
            System.out.println("回滚事务");
            e.printStackTrace();
        }
        return null;
    }
}

如图所示:在这里插入图片描述

3.2 更改目标实现类

代码如下:

package com.txw.service.impl;

import com.txw.service.UserService;
/**
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public class UserServiceImpl implements UserService {
    
    

    /**
     * 保存
     * @param name
     */
    @Override
    public void save(String name) {
    
    
        System.out.println("处理业务逻辑,调用DAO~~~");
    }

    /**
     * 根据id删除
     * @param id
     */
    @Override
    public void delete(String id) {
    
    
        System.out.println("处理业务逻辑,调用DAO~~~");
    }

    /**
     * 修改
     */
    @Override
    public void update() {
    
    
        System.out.println("处理业务逻辑,调用DAO~~~");
    }

    /**
     * 根据名字查询所有
     * @param name
     * @return
     */
    @Override
    public String findAll(String name) {
    
    
        System.out.println("处理业务逻辑,调用DAO~~~");
        return name;
    }

    /**
     * 根据id查询一个
     * @param id
     * @return
     */
    @Override
    public String findOne(String id) {
    
    
        System.out.println("处理业务逻辑,调用DAO~~~");
        return id;
    }
}

如图所示:在这里插入图片描述

3.3 配置静态代理类

代码如下:

<?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="userService" class="com.txw.service.impl.UserServiceImpl"></bean>
    <!--配置代理类-->
    <bean id="serviceStaticProxy" class="com.txw.util.UserServiceStaticProxy">
        <!--注入目标对象-->
        <property name="userService" ref="userService"/>
    </bean>
</beans>

如图所示:在这里插入图片描述

3.4 调用代理方法

代码如下:

package com.txw.test;

import com.txw.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * 测试
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public class UserServiceTest {
    
    

    @Test
    public void test(){
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        UserService serviceStaticProxy = (UserService) context.getBean("serviceStaticProxy");
        serviceStaticProxy.save("Adair");
    }
}

如图所示:在这里插入图片描述

新的问题:往往在开发我们书写的不仅仅是一个业务层,两个业务层,而我们的业务层会有很多,如果为每一个业务层开发一个静态代理类,不仅没有减轻工作量,甚至让我们的工作量多了一倍不止怎么解决以上这个问题呢?
解决方案: 为业务层在运行过程中动态创建代理类,通过动态代理类去解决我们现有业务层中业务代码冗余的问题

4 动态代理的原理

通过jdk提供的Proxy这个类,动态为现有的业务生成代理类
参数一:当前线程类加载器。
参数二:生成代理类的接口类型。
参数三:通过代理类对象调用方法时会优先进入参数三中的invoke方Proxy.newProxyInstance(loader, interfaces, h);//返回值就是动态代理对象。

演示的代码如下:

package com.txw.test;

import com.txw.service.UserService;
import com.txw.service.impl.UserServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 动态测试类
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public class TestDynamicProxy {
    
    
    public static void main(String[] args) {
    
    
        final UserService userService =  new UserServiceImpl();
        // 参数1:当前线程类加载器
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        // 参数2:
        Class[] classes =  new Class[]{
    
    UserService.class};
        // 参数3:
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(contextClassLoader, classes, new InvocationHandler() {
    
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                try{
    
    
                    System.out.println("开启事务");             // 附加操作
                    Object invoke = method.invoke(userService, args);
                    System.out.println("提交事务");             // 附加操作
                    return invoke;
                }catch (Exception e){
    
    
                    System.out.println("回滚事务");             // 附加操作
                }
                return null;
            }
        });
        userServiceProxy.save("小黑");
    }
}

在这里插入图片描述

5 AOP (Aspect Oriented Programming)编程

通知(Advice): 除了目标方法以外的操作都称之为通知
切入点(PointCut): 要为哪些类中的哪些方法加入通知
切面(Aspect): 通知 + 切入点

5.1 通知分类

如图所示:在这里插入图片描述

5.2 编程步骤

1.引入依赖,如图所示:
在这里插入图片描述
2.开发通知类
1.编写的EmpService的代码如下:

package com.txw.service;

/**
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public interface EmpService {
    
    
    public void ma();
    public void mb(String name);
    public String mc(String id);
}

如图所示:
在这里插入图片描述
2.编写的EmpServiceImpl的代码如下:

package com.txw.service.impl;

import com.txw.service.EmpService;
/**
 * 核心业务对象 被代理的对象 目标对象
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public class EmpServiceImpl implements EmpService {
    
    
    @Override
    public void ma() {
    
    
        System.out.println("处理 ma 核心业务逻辑");
    }

    @Override
    public void mb(String name) {
    
    
        System.out.println("处理 mb 核心业务逻辑");
    }

    @Override
    public String mc(String id) {
    
    
        System.out.println("处理 mc 核心业务逻辑");
        return id;
    }
}

如图所示:在这里插入图片描述
3.配置切面
在spring1.xml添加的代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
   <!--管理 核心业务对象 目标对象-->
    <bean id="empService" class="com.txw.service.impl.EmpServiceImpl"></bean>
    <!--管理 通知-->
    <bean id="boforeAdivce" class="com.txw.before.MyBoforeAdivce"></bean>
    <!--配置切面-->
    <aop:config>
        <!--配置切入点
           id: 切入点表示 唯一
           execution:切入点表达式
         -->
        <aop:pointcut id="pc1" expression="execution(* com.txw.service.impl.*.*(..))"/>
            <!--配置切入点 额外功能
                advice-ref:指定添加的通知
                pointcut-ref:指定要使用切入点
            -->
        <aop:advisor advice-ref="boforeAdivce" pointcut-ref="pc1"/>
    </aop:config>
</beans>

如图所示:在这里插入图片描述
4.启动工厂测试
.编写SpringTest的代码如下:

package com.txw.test;

import com.txw.service.EmpService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * 测试
 * @author Adair
 * E-mail: [email protected]
 */
public class SpringTest {
    
    
    @Test
    public void test(){
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("spring1.xml");
         // 当前获取的核心业务对象 是通过动态代理生成的代理对象
        EmpService empService = (EmpService) context.getBean("empService");
        empService.ma();
    }
}

如图所示:在这里插入图片描述

5.3 前置通知的使用

代码如下:

package com.txw.before;

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public class MyBoforeAdivce implements MethodBeforeAdvice {
    
    
    /**
     *
     * @param method 目标类中当前调用的方法对象
     * @param objects 当前调用方法的参数
     * @param o 目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
    
    
        System.out.println("日志通知类记录方法的名字为:" +method);
    }
}

如图所示:
在这里插入图片描述

5.4 环绕通知的使用

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

package com.txw.before;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * 环绕通知
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public class MyMethodInterceptor implements MethodInterceptor {
    
    
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    
    
        //  书写额外功能
        long begin = System.currentTimeMillis();
        System.out.println("方法开始执行!!!");
        // 调用核心业务对象 核心服务方法
        Object result = methodInvocation.proceed();// 放行 执行核心服务方法 等价于动态代理 method.invoke(new xxxServiceImpl,args)
        System.out.println("方法即将结束!!!");
        long end = System.currentTimeMillis();
        System.out.println("方法运行时长为:" + (end =begin));
       /* System.out.println("方法名:" +methodInvocation.getMethod().getName());
        System.out.println("参数");
        for (Object argument : methodInvocation.getArguments()) {
            System.out.println((String) argument);
        }*/
        return result;
    }
}

如图所示:在这里插入图片描述
在spring1.xml添加的代码如下:

 <!--环绕通知-->
    <bean  id="myMethodInterceptor" class="com.txw.before.MyMethodInterceptor"></bean>
    <!--配置切面-->
    <aop:config>
        <aop:pointcut id="PC1" expression="execution(* com.txw.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="myMethodInterceptor" pointcut-ref="pc1"/>
    </aop:config>

如图所示:在这里插入图片描述

5.5 返回后通知

代码如下:

package com.txw.before;

import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
 * 后置通知
 * @author Adair
 * E-mail: [email protected]
 */
@SuppressWarnings("all")  // 注解警告信息
public class AtferReturnimgAdvice implements AfterReturningAdvice {
    
    

    /**
     * 主体逻辑:此中的过程在核心业务执行之后执行
     * @param o 目标方法返回值 如果方法是void 则o为null
     * @param method 目标方法
     * @param objects 方法参数列表
     * @param o1 目标
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
    
    

    }
}

如图所示:在这里插入图片描述
在spring1.xml添加的代码如下:

<!--后置通知-->
    <bean id="atferReturnimgAdvice" class="com.txw.before.AtferReturnimgAdvice"></bean>
    <!--配置切面-->
    <aop:config>
        <aop:pointcut id="PC1" expression="execution(* com.txw.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="atferReturnimgAdvice" pointcut-ref="pc1"/>
    </aop:config>

如图所示:在这里插入图片描述

5.6 异常通知在这里插入图片描述在spring1.xml添加的代码,如图所示:在这里插入图片描述## 6 切入点表达表

6.1 execution方法级别的切入点表达式

如图所示:
在这里插入图片描述

注意:方法级别的切入点表达式尽可能精准,否则程序运行可能出现异常`

6.2 within类级别的切入点表达式

1.语法如下:

within(.)

2.演示的代码,如图所示:在这里插入图片描述

注意:within的效率高于execution表达式,推荐使用within表达式

猜你喜欢

转载自blog.csdn.net/weixin_40055163/article/details/120994235