这是一个沉淀的过程,大概第一次接触Spring是在去年的这个时候,当初在实训,初次接触Java web,直接学习SSM框架(当是Servlet都没有学),因而,养成了一个很很差的学习习惯,就是“照猫画虎”。别人作什么,照着样子就是了,没有任何的思考,这样的学习习惯确定不会走太远。如今我产生不少疑惑,这是什么?为何这么作?如何作的更好?所以此次笔记的主题就是《这是什么?》java
PointCut切入点:已经被加强的链接点。例如:addUser()web
Aspect切面:是切入点pointcut和通知advice的结合
一个线是一个特殊的面。
一个切入点和一个通知,组成成一个特殊的面。
spring
测试
sql
public interface UserService { void addUser(); void updateUser(); void deleteUser(); }
public class MyAspect { public void before(){ System.out.println("this is before"); } public void after(){ System.out.println("this is after"); } }
public class MyBeanFactory { public static UserService createService(){ //1 目标类 final UserService userService = new UserServiceImpl(); //2 切面类 final MyAspect myAspect = new MyAspect(); /** * 3 代理类:将目标类(切入点)和 切面类(通知) 结合 --> 切面 * Proxy.newProxyInstance * 参数1:loader ,类加载器,动态代理类 运行时建立,任何类都须要类加载器将其加载到内存。 * 通常状况:当前类.class.getClassLoader(); * 目标类实例.getClass().get... * 参数2:Class[] interfaces 代理类须要实现的全部接口 * 方式1:目标类实例.getClass().getInterfaces() ;注意:只能得到本身接口,不能得到父元素接口 * 方式2:new Class[]{UserService.class} * 例如:jdbc 驱动 --> DriverManager 得到接口 Connection * 参数3:InvocationHandler 处理类,接口,必须进行实现类,通常采用匿名内部 * 提供 invoke 方法,代理类的每个方法执行时,都将调用一次invoke * 参数31:Object proxy :代理对象 * 参数32:Method method : 代理对象当前执行的方法的描述对象(反射) * 执行方法名:method.getName() * 执行方法:method.invoke(对象,实际参数) * 参数33:Object[] args :方法实际参数 * */ UserService proxService = (UserService) Proxy.newProxyInstance( MyBeanFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //前执行 myAspect.before(); //执行目标类的方法 Object obj = method.invoke(userService,args); //后方法 myAspect.after(); return obj; } } ); return proxService; } } }
@Test public void demo01(){ UserService userService = MyBeanFactory.createService(); userService.addUser(); userService.updateUser(); userService.deleteUser(); }
public class MyBeanFactory { public static UserService createService(){ //1 目标类 final UserService userService = new UserServiceImpl(); //2 切面类 final MyAspect myAspect = new MyAspect(); // 3.代理类 ,采用cglib ,底层建立目标类的子类 // 3.1 核心类 Enhancer enhancer = new Enhancer(); // 3.2 肯定父类 enhancer.setSuperclass(userService.getClass()); /** * 3.3 设置回调函数 , MethodInterceptor接口 等效 jdk InvocationHander接口 * intercept() 等效 jdk invoke() * 参数一、参数二、参数3:以invoke同样 * 参数4:methodProxy 方法的代理 */ enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { //前执行 myAspect.before(); //执行目标类方法 Object obj = method.invoke(userService,objects); // * 执行代理类的父类,执行目标类(目标类和代理类 父子关系) // methodProxy.invokeSuper(o,objects); //后执行 myAspect.after(); return obj; } }); //3.4 建立代理 UserServiceImpl proxService = (UserServiceImpl) enhancer.create(); return proxService; } }
环绕通知,必须手动执行目标方法 try{ //前置通知 //执行目标方法 //后置通知 } catch(){ //抛出异常通知 }
public interface UserService { void addUser(); void updateUser(); void deleteUser(); }
/** * 切面类中肯定通知,须要实现不一样接口,接口就是规范,从而就肯定方法名称。 * 采用“环绕通知” MethodInterceptor */ public class MyAspect implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println("this is before"); //手动执行目标方法 Object obj = methodInvocation.proceed(); System.out.println("this is after"); return obj; } }
<!-- 1 建立目标类--> <bean id="userService" class="com.springlearning.spring_proxy.UserServiceImpl"></bean> <!-- 2 建立切面类--> <bean id="myAspect" class="com.springlearning.spring_proxy.MyAspect"></bean> <!-- 3 建立代理类 * 使用工厂bean FactoryBean ,底层调用getObject() 返回特殊bean * ProxyFactoryBean 用于建立代理工厂bean ,生成特殊代理对象 interfaces : 肯定接口们 经过<array> 能够设置多个值 只有一个值时, value="" target : 肯定目标类 interceptorNames : 通知 切面类的名称,类型String[], 若是设置一个值 value="" optimize :强制使用cglib <property name="optimize" value="true"></property> 底层机制 若是目标类有接口,采用jdk动态代理 若是没有接口,采用cglib 字节码加强 若是声明 optimize = true ,不管是否有接口 ,都采用cglib --> <bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="com.springlearning.spring_proxy.UserService"></property> <property name="target" ref="userService"></property> <property name="interceptorNames" value="myAspect"></property> </bean>
@Test public void demo01(){ String xmlPath = "com/springlearning/spring_proxy/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); //得到代理类 UserService userService = (UserService) applicationContext.getBean("proxyService"); userService.addUser(); userService.updateUser(); userService.deleteUser(); }
在spring-aop-5.0.4版本中包含了aopalliance和aspectj
数据库
这里我在调试的时候出了问题,其中在spring-aop-5.0.4版本中包含了aopalliance,而我看的教学是3.x的版本,须要导入另外的aopalliance包,还有错把aspectjrt 当成 aspectjweaver,然而两个包有着不一样的功能。express
<?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"> <!-- 1 建立目标类 --> <bean id="userService" class="com.springlearning.spring_proxy.UserServiceImpl"></bean> <!-- 2 建立切面类(通知)--> <bean id="myAspect" class="com.springlearning.spring_proxy.MyAspect"></bean> <!-- 3 aop编程 3.1 导入命名空间 3.2 使用 <aop:config> 进行配置 proxy-target-class="true" 声明时使用cglib代理 <aop:pointcut> 切入点 , 从目标对象得到具体方法 <aop:advisor> 特殊的切面, 只用一个通知 和一个切入点 advice-ref 通知引用 pointcut-ref 切入点引用 3.3 切入点表达式 execution(* com.springlearning.spring_proxy.*.(..)) 选择方法 返回值任意 包 类名称任意 方法名任意 参数任意 --> <aop:config proxy-target-class="true"> <aop:pointcut id="myPointCut" expression="execution(* com.springlearning.spring_proxy.*.*(..))"/> <aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"></aop:advisor> </aop:config> </beans>
@Test public void demo01(){ String xmlPath = "com/springlearning/spring_proxy/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); //得到代理类 UserService userService = (UserService) applicationContext.getBean("userService"); userService.addUser(); userService.updateUser(); userService.deleteUser(); }
综合 1
execution(* com.springlearning.proxy..service...*(..))apache
综合 2
<aop:pointcut expression="execution(* com.springlearning.WithCommit.(..)) ||
execution(* com.springlearning.Service.(..))" id="myPointCut"/>编程
环绕 try{ //前置:before //手动执行目标方法 //后置:afterRetruning } catch(){ //抛出异常 afterThrowing } finally{ //最终 after }
/** * 切面类,含有多个通知 */ public class MyAspect { public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知:" + joinPoint.getSignature().getName()); } public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); } public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手动执行目标方法 Object obj = joinPoint.proceed(); System.out.println("后"); return obj; } public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable){ System.out.println("抛出异常通知:" + throwable.getMessage()); } public void myAfter(JoinPoint joinPoint){ System.out.println("最终通知"); } }
<!-- 1 建立目标类 --> <bean id="userService" class="com.springlearning.aspectj.UserServiceImpl"></bean> <!-- 2 建立切面类(通知)--> <bean id="myAspect" class="com.springlearning.aspectj.MyAspect"></bean> <!-- 3 aop编程 <aop:aspect> 将切面类 声明“切面”,从而得到通知(方法) ref 切面类引用 <aop:pointcut> 声明一个切入点,全部通知均可以使用。 expression 切入点表达式 id 名称,用于其余通知引用 --> <aop:config> <aop:aspect ref="myAspect"> <aop:pointcut id="myPointcut" expression="execution(* com.springlearning.aspectj.UserServiceImpl.*(..))"/> <!-- 3.1 前置通知 <aop:before method="" pointcut="" pointcut-ref=""/> method : 通知,及方法名 pointcut :切入点表达式,此表达式只能当前通知使用。 pointcut-ref : 切入点引用,能够与其余通知共享切入点。 通知方法格式:public void myBefore(JoinPoint joinPoint){ 参数1:org.aspectj.lang.JoinPoint 用于描述链接点(目标方法),得到目标方法名等 例如: <aop:before method="myBefore" pointcut-ref="myPointCut"/> --> <!-- 3.2后置通知 ,目标方法后执行,得到返回值 <aop:after-returning method="" pointcut-ref="" returning=""/> returning 通知方法第二个参数的名称 通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){ 参数1:链接点描述 参数2:类型Object,参数名 returning="ret" 配置的 例如: <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" /> --> <!-- 3.3 环绕通知 <aop:around method="" pointcut-ref=""/> 通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ 返回值类型:Object 方法名:任意 参数:org.aspectj.lang.ProceedingJoinPoint 抛出异常 执行目标方法:Object obj = joinPoint.proceed(); 例如: <aop:around method="myAround" pointcut-ref="myPointCut"/> --> <!-- 3.4 抛出异常 <aop:after-throwing method="" pointcut-ref="" throwing=""/> throwing :通知方法的第二个参数名称 通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ 参数1:链接点描述对象 参数2:得到异常信息,类型Throwable ,参数名由throwing="e" 配置 例如: <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/> --> <!-- 3.5 最终通知--> <aop:after method="myAfter" pointcut-ref="myPointcut"></aop:after> </aop:aspect> </aop:config>
<!-- 1 建立目标类 --> <bean id="userService" class="com.springlearning.aspectj.UserServiceImpl"></bean> <!-- 2 建立切面类(通知)--> <bean id="myAspect" class="com.springlearning.aspectj.MyAspect"></bean>
@Component public class MyAspect { @Service("userService") public class UserServiceImpl implements UserService {
<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/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"> <!-- 1. 扫描 注解类 --> <context:component-scan base-package="com.springlearning.aspectj"></context:component-scan>
<!-- 2.肯定 aop注解生效 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<aop:aspect ref="myAspect"> <!--被下面注解替换 -->
@Component @Aspect //添加注解 public class MyAspect {
<aop:before method="myBefore" pointcut="execution(* com.ithspringlearning.aspectj.UserServiceImpl.*(..))"/> <!--被下面注解替换 -->
//切入点当前有效 @Before("execution(* com.springlearning.aspectj.UserServiceImpl.*(..))") public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知:" + joinPoint.getSignature().getName()); }
<aop:pointcut id="myPointcut" expression="execution(* com.springlearning.aspectj.UserServiceImpl.*(..))"/> <!--被下面注解替换 -->
//声明公共切入点 @Pointcut("execution(* com.springlearning.aspectj.UserServiceImpl.*(..))") private void myPointCut(){}
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
@AfterReturning(value = "myPointCut()",returning = "ret") public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); }
<aop:around method="myAround" pointcut-ref="myPointCut"/>
@Around(value = "myPointCut()") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手动执行目标方法 Object obj = joinPoint.proceed(); System.out.println("后"); return obj; }
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="throwable"/>
@AfterThrowing(value = "myPointCut()" ,throwing = "throwable") public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable){ System.out.println("抛出异常通知:" + throwable.getMessage()); }
/** * 切面类,含有多个通知 */ @Component @Aspect public class MyAspect { //声明公共切入点 @Pointcut("execution(* com.springlearning.aspectj.UserServiceImpl.*(..))") private void myPointCut(){} // @AfterReturning(value = "myPointCut()",returning = "ret") public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret); } //切入点当前有效 // @Before("execution(* com.springlearning.aspectj.UserServiceImpl.*(..))") public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知:" + joinPoint.getSignature().getName()); } // @Around(value = "myPointCut()") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手动执行目标方法 Object obj = joinPoint.proceed(); System.out.println("后"); return obj; } // @AfterThrowing(value = "myPointCut()" ,throwing = "throwable") public void myAfterThrowing(JoinPoint joinPoint,Throwable throwable){ System.out.println("抛出异常通知:" + throwable.getMessage()); } @After(value = "myPointCut()") public void myAfter(JoinPoint joinPoint){ System.out.println("最终通知"); } }
<!-- 1. 扫描 注解类 --> <context:component-scan base-package="com.springlearning.aspectj"></context:component-scan> <!-- 2.肯定 aop注解生效 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
create table t_user( id int primary key , username varchar(50), password varchar(32) );
public class User { private Integer id; private String username; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
@Test public void demo(){ //1 建立数据源(链接池) dbcp BasicDataSource dataSource = new BasicDataSource(); // * 基本4项 dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver"); dataSource.setUrl("jdbc:oracle:thin:@localhost:1530/orcl"); dataSource.setUsername("sys as sysdba"); dataSource.setPassword("sys"); //2 建立模板 JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(dataSource); //3 经过api操做 jdbcTemplate.update("insert into t_user(id,username,password) values(?,?,?)","6", "tom","998"); }
<!-- 建立数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property> <property name="url" value="jdbc:oracle:thin:@localhost:1530/orcl"></property> <property name="username" value="sys as sysdba"></property> <property name="password" value="sys"></property> </bean> <!-- 建立模板 ,须要注入数据源--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置dao --> <bean id="userDao" class="com.springlearning.jdbctemplate.UserDao"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean>
<!-- 建立C3P0数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="oracle.jdbc.driver.OracleDriver"></property> <property name="jdbcUrl" value="jdbc:oracle:thin:@localhost:1530/orcl"></property> <property name="user" value="sys as sysdba"></property> <property name="password" value="sys"></property> </bean>
package com.springlearning.jdbctemplate; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.support.JdbcDaoSupport; import java.util.List; /** * @ClassName:UserDao * @author: donkey-boy * @date:2019/7/19 10:52 */ public class UserDao extends JdbcDaoSupport { /** * @Author: donkey-boy * @Description: 更新操做 * @Param: user * @Return: void * @Date: 2019/7/19 15:04 */ public void update(User user){ String sql = "UPDATE t_user SET username=?,password=? WHERE id=?"; Object[] args = {user.getUsername(),user.getPassword(),user.getId()}; this.getJdbcTemplate().update(sql,args); } /** * @Author: donkey-boy * @Description: 查询所有 * @Param: [] * @Return: java.util.List<com.springlearning.jdbctemplate.User> * @Date: 2019/7/19 15:06 */ public List<User> findAll(){ List<User> userList = this.getJdbcTemplate().query("select * from t_user", BeanPropertyRowMapper.newInstance(User.class)); return userList; } /** * @Author: donkey-boy * @Description: 获取单个用户 * @Param: [id] * @Return: com.springlearning.jdbctemplate.User * @Date: 2019/7/19 15:06 */ public User getUser(int id){ User user = this.getJdbcTemplate().queryForObject("select * from t_user where id=?", BeanPropertyRowMapper.newInstance(User.class), id); return user; } }
<!-- 配置dao * dao 继承 JdbcDaoSupport,以后只须要注入数据源,底层将自动建立模板 --> <!-- 配置dao --> <bean id="userDao" class="com.springlearning.jdbctemplate.UserDao"> <property name="dataSource" ref="dataSource"></property> </bean>
jdbc.driverClass=oracle.jdbc.driver.OracleDriver jdbc.jdbcUrl=jdbc:oracle:thin:@localhost:1530/orcl jdbc.user=sys as sysdba jdbc.password=sys
<!-- 加载配置文件 "classpath:"前缀表示 src下 在配置文件以后经过 ${key} 得到内容 --> <context:property-placeholder location="classpath:com/springlearning/jdbctemplate/jdbcInfo.properties"></context:property-placeholder> <!-- 建立C3P0数据源 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean>
笔记源自传智播客Spring教学设计模式