4.3 Spring API接口实现
- Aop是什么?一般情况下,在程序执行时,是从上到下执行的
- 如果要在执行的某个点,插入一个功能,如何实现?
- 比如一个系统,我需要加入逻辑删除,我需要增加日志功能,难道我去修改原有代码?
- 这就需要我们使用Aop来实现横向切入,增加功能!
我们以增加日志功能为例,介绍Aop的实现流程之一 —— 基于Spring API接口实现
编写一个Service
public interface BookService { void add(); void delete(); void update(); void select(); }
编写Service的实现类
public class BookServiceImpl implements BookService { public void add() { System.out.println("增加了一本书"); } public void delete() { System.out.println("删除了一本书"); } public void update() { System.out.println("修改了一本书"); } public void select() { System.out.println("查询了一本书"); } }
编写方法前日志
public class BeforeLog implements MethodBeforeAdvice { /** * method要执行的目标对象的方法 * args 参数 * target 目标对象 */ public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName() + " 的 "+method.getName() + " 被执行了!"); } }
编写方法后日志
public class AfterLog implements AfterReturningAdvice { /** * returnValue 返回对象 * method要执行的目标对象的方法 * args 参数 * target 目标对象 */ public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了 " +method.getName() + " 方法, 返回结果为" + returnValue); } }
配置文件配置切面、切入点
<?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:c="http://www.springframework.org/schema/c" xmlns:p="http://www.springframework.org/schema/p" 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="bookService" class="zwz.demo1.service.BookServiceImpl"/> <bean id="beforeLog" class="zwz.demo1.log.BeforeLog"/> <bean id="afterLog" class="zwz.demo1.log.AfterLog"/> <!--配置AOP--> <aop:config> <!-- 切入点--> <aop:pointcut id="pointcut" expression="execution(* zwz.demo1.service.BookServiceImpl.*(..))"/> <!--执行环绕增加 ,将beforeLog切入到pointcut中 --> <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>
测试
@Test public void testAop1(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); BookService bookService = (BookService) context.getBean("bookService"); bookService.add(); }
这样,我们就实现了在执行某个方法的时候,输出日志的功能了
输出结果
zwz.demo1.service.BookServiceImpl 的 add 被执行了! 增加了一本书 执行了 add 方法, 返回结果为null Process finished with exit code 0
4.4 自定义类实现
接下来是基于自定义类接口实现,和spring的API实现一样,先写业务的实现接口和实现类
public interface BookService2 { void add(); void delete(); void update(); void select(); }
public class BookServiceImpl2 implements BookService2 { public void add() { System.out.println("增加了一本书"); } public void delete() { System.out.println("删除了一本书"); } public void update() { System.out.println("修改了一本书"); } public void select() { System.out.println("查询了一本书"); } }
和spring API方法一致,编写两个切入日志类
public class BeforeLog implements MethodBeforeAdvice { /** * method要执行的目标对象的方法 * args 参数 * target 目标对象 */ public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName() + " 的 "+method.getName() + " 被执行了!"); } }
public class AfterLog implements AfterReturningAdvice { /** * returnValue 返回对象 * method要执行的目标对象的方法 * args 参数 * target 目标对象 */ public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了 " +method.getName() + " 方法, 返回结果为" + returnValue); } }
编写自定义切入类
public class DiyPointCut { public void before(){ System.out.println("==============方法执行前=============="); } public void after(){ System.out.println("==============方法执行后=============="); } }
配置文件
<bean id="bookService2" class="zwz.demo2.service.BookServiceImpl2"/> <!--方式二:采用自定义类 配置AOP--> <bean id="diy" class="zwz.demo2.diy.DiyPointCut"/> <aop:config> <!-- 自定义切面 ref要引用的类--> <aop:aspect ref="diy"> <!-- 切入点--> <aop:pointcut id="point" expression="execution(* zwz.demo2.service.BookServiceImpl2.*(..))"/> <!-- 通知--> <aop:before method="before" pointcut-ref="point"/> <aop:after method="after" pointcut-ref="point"/> </aop:aspect> </aop:config>
测试
@Test public void testAop2(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); BookService2 bookService2 = (BookService2) context.getBean("bookService2"); bookService2.add(); }
==============方法执行前============== 增加了一本书 ==============方法执行后==============
4.5 Java注解实现
和之前两个方法一样,先写Service服务层的接口和实现类
public interface BookService3 { void add(); void delete(); void update(); void select(); }
public class BookServiceImpl3 implements BookService3 { public void add() { System.out.println("增加了一本书"); } public void delete() { System.out.println("删除了一本书"); } public void update() { System.out.println("修改了一本书"); } public void select() { System.out.println("查询了一本书"); } }
切面类
@Aspect //标注这个类是一个切面 public class PointCut { @Before("execution(* zwz.demo3.service.BookServiceImpl3.*(..))") public void before(){ System.out.println("==============方法执行前=============="); } @After("execution(* zwz.demo3.service.BookServiceImpl3.*(..))") public void after(){ System.out.println("==============方法执行后=============="); } }
配置文件
<!--方式三:采用注解 配置AOP--> <bean id="bookService3" class="zwz.demo3.service.BookServiceImpl3"/> <bean id="pointCut" class="zwz.demo3.cut.PointCut"/> <!--开启注解支持 默认JDK实现(proxy-target-class="false")|| 也有cglib--> <aop:aspectj-autoproxy/>
测试类
@Test public void testAop3(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); BookService3 bookService3 = (BookService3) context.getBean("bookService3"); bookService3.add(); }
输出结果
==============方法执行前============== 增加了一本书 ==============方法执行后==============
五、总结
5.1 总结&附录
spring是一个粘合剂,可以整合其他优秀的框架,大大简化了软件开发的复杂度
我认为spring的核心内容:
- IOC控制反转
- AOP面向切面编程
其他内容比如整合mybatis、声明式事务,在后续再写。
5.2 模板文件
applicationContext.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:context="http://www.springframework.org/schema/context" xmlns:c="http://www.springframework.org/schema/c" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </beans>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <package name="zwz.pojo"/> </typeAliases> <environments default="zwz"> <environment id="zwz"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper class="zwz.mapper.UserMybatisMapper"></mapper> </mappers> </configuration>
5.3 spring配置标签
bean
bean用来注册实体类,可以注入相应的变量值
<bean id="user" class="zwz.pojo.User"> <property name="name" value="ZWZ"/> <property name="age" value="18"/> </bean>
alias
设置别名,设置后原名和别名都可以使用
bean 中的 name 属性也可以做别名,而且功能更加强大
<bean id="user" class="zwz.pojo.User"> <property name="name" value="ZWZ"/> <property name="age" value="18"/> </bean> <alias name="user" alias="userZwz"/>
可以在测试类中这样调用别名bean
// ApplicationContext Spring上下文对象 | ClassPathXmlApplicationContext 从配置文件中去取 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 从容器中取出user对象 User user = (User) context.getBean("userZwz");
import
在团队开发中,每个人都需要拥有自己的配置文件,可以通过import标签将多个配置文件整合成一个
如张三的配置文件 —— bean1.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="zwz.pojo.User"> <!-- property相当于给某个变量设置一个值--> <property name="name" value="ZWZ"/> <property name="age" value="18"/> </bean> <alias name="user" alias="userZwz"/> </beans>
团队整体的配置文件 —— applicationContext.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"> <import resource="bean1.xml"/> </beans>
context
自动装配bean
<context:annotation-config/>
扫描指定包的组件
<!--指定要扫描的包 这个包下的注解就会生效--> <context:component-scan base-package="zwz"/>