目录
Spring表达式语言SpELSpEL字面量引用 Bean属性和方法SpEL支持的运算符号静态代理静态代理角色分析代码分析使用静态代理的优缺点动态代理AOPAOP简介AOP 术语Spring AOP基于 aspectJ 注解配置AOP基于xml配置AOP基于XML和 注解配置比较Spring中的事务管理事务简介Spring 中的事务管理器用事务通知声明式地管理事务用 Transactional 注解声明式地管理事务事务传播属性并发事务所导致的问题事务的隔离级别超时和只读属性
正文
(1)整数:<property name="count" value="#{5}"/>
(2)小数:<property name="frequency" value="#{89.7}"/>
(3)科学计数法:<property name="capacity" value="#{1e4}"/>
(4)String可以使用单引号或者双引号作为字符串的定界符号:<property name=“name” value="#{'Chuck'}"/> 或 <property name='name' value='#{"Chuck"}'/> Boolean:<property name="enabled" value="#{false}"/>
(1) (2)
(1) (2)
抽象角色:一般使用抽象类或接口来实现; 真实角色:被代理的角色; 代理角色:代理真实角色,代理真实角色后一般还会做一些附属操作; 客户:使用代理角色来进行一些操作;
Rent.java—-抽象角色
package com.ren.staticProxy;/* * 抽象角色:真实角色和代理角色的公共方法; */public interface Rent { public void rent();}Host.java—-真实角色
package com.ren.staticProxy;/* * 只要写纯粹属于自己的业务就好了 */public class Host implements Rent{ @Override public void rent() { System.out.println("出租房屋"); }}Proxy.java—-代理角色
package com.ren.staticProxy;/* * 代理:除了依赖真实角色外还有自己的业务逻辑 */public class Proxy implements Rent { private Host host; //通过setter方法注入来获取 host public void setHost(Host host) { this.host = host; } //带顾客看房 public void findHouse() { System.out.println("代理带顾客找房"); } //收中介费 public void fare() { System.out.println("交钱"); } @Override public void rent() { findHouse(); host.rent();//依赖雇主 fare(); }}Client.java—-客户
package com.ren.staticProxy;/* *客户是 实现,也可以通过配置文件实现 */public class Client { public static void main(String[] args) { Host host = new Host(); Proxy proxy = new Proxy(); proxy.setHost(host); proxy.rent(); } }优点:
是真实角色的业务更加纯粹,不在去关注一些公共的事情;
公共的业务由代理来实现,实现了业务分工;
公共业务发生扩展时变得更加集中和方便;
缺点:
类多了,多了代理类,工作量变大了,开发效率降低了;
因为静态代理的缺点,所以引出了动态代理;
1.动态代理和静态代理的角色相同; 2.动态代理的代理类是自动生成的; 3.动态代理分为两类: (1)基于接口的动态代理:JDK动态代理; (2)基于类的动态代理:cglib动态代理; 现在javasist生成动态代理; 4.jdk动态代理—Proxy和InvocationHandler接口: Rent.java —-抽象角色
package com.ren.dynamicProxy;/* * 抽象角色:真实角色和代理角色的公共方法; */public interface Rent { public void rent();}Host.java —-真实角色
package com.ren.dynamicProxy;/* * 只要写纯粹属于自己的业务就好了 */public class Host implements Rent{ @Override public void rent() { System.out.println("出租房屋"); }}ProxyInvocationHander.java —-生成代理类的: 可以代理所有接口;
package com.ren.dynamicProxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * 1).InvocationHandler 是代理实例的调用处理程序 实现的接口。 * 2). ProxyInvocationHander这个类只能代理接口 * */public class ProxyInvocationHander implements InvocationHandler{ /* * 1.动态代理代理Rent接口 * private Rent rent; * public void setRent(Rent rent) { * this.rent = rent; * } *2.修改:是动态代理代理所有接口:如下: */ private Object target;//目标对象---真实对象(代理类所代理的对象) public void setTarget(Object target) { this.target = target; } /** * 生成代理类 * 参数三:InvocationHandler * */ public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } /* * (non-Javadoc) * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) * 1)invoke方法:在代理实例上处理方法调用并返回结果。 * 2)参数:proxy - 代理类(在其上调用方法的代理实例) * method - 代理类的调用处理程序的方法对象(对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。) * args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //findHouse(); //result相当于调用代理类和真实角色公共的方法的返回值; Object result = method.invoke(target, args);//第一个参数是真实对象,即该方法所在的对象; //fare(); return result; } public void log(String methodName) { System.out.println("调用"+methodName+"方法"); }// //带顾客看房// public void findHouse() {// System.out.println("代理带顾客找房");// }// //收中介费// public void fare() {// System.out.println("交钱");// }// }Client.java—-客户
package com.ren.dynamicProxy;import com.ren.dao.service.UserService;import com.ren.dao.service.UserServiceImpl;/* *客户是 实现,也可以通过配置文件实现 */public class Client { public static void main(String[] args) {// Host host = new Host();// ProxyInvocationHander pih = new ProxyInvocationHander();// pih.setTarget(host);// Rent proxy = (Rent) pih.getProxy();// proxy.rent(); UserService userService = new UserServiceImpl(); ProxyInvocationHander pih = new ProxyInvocationHander(); pih.setTarget(userService); UserService proxy = (UserService) pih.getProxy(); proxy.getUser(); } }总结:动态代理一般代理某一类业务;一个动态代理可以代理多个类;
传统编程模式:
AOP编程模式:在不改变原有代码的基础上增加功能。
AOP 的好处: (1)每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级 (2)业务模块更简洁, 只包含核心业务代码.AOP实际上是为了解决真实角色中的代码重复问题,即方法共有的业务,比如:日志、安全、异常、缓存、权限、事务等等;这些业务又叫领域业务—–领域模型;领域模型比如: 支付业务—接入第三方接口—安全检查—身份验证—资金验证—支付;Spring 的AOP可以看成是动态代理的框架;AspectJ:Java 社区里最完整最流行的 AOP 框架;
在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP;
Spring 中使用AOP需要导包:aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar
切面:是一个带有 @Aspect 注解的 Java 类;
通知:是标注有某种注解的简单的 Java 方法; AspectJ 支持 5 种类型的通知注解: @Before: 前置通知, 在方法执行之前执行; @After: 后置通知, 在方法执行之后执行(不管是否抛出异常); @AfterRunning: 返回通知, 在方法返回结果之后执行; @AfterThrowing: 异常通知, 在方法抛出异常之后; @Around: 环绕通知, 围绕着方法执行;
指定切面的优先级:@Order(序号越小越高);(或通过实现 Ordered 接口, getOrder() 方法的返回值越小越高)
切入点表达式: (1)eg:execution(* com.ren.dao.service.impl..(..)):第一个 * 代表任意修饰符及任意返回值.第二个 * 代表任意类. 第三个 * 代表任意方法. .. 匹配任意数量的参数.
(2)在 AspectJ 中, 切入点表达式可以通过操作符 &&, ||, ! 结合起来.
例子: 自定义切面:MyLog.java:
package com.ren.log;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.core.annotation.Order;/* * 通过注解实现AOP */@Aspect//标志是一个切面@Order(0)//指定优先级public class MyLog { //前置通知 @Before("execution(* com.ren.dao.service.impl.*.*(..))") public void before() { System.out.println("方法执行前。。"); } //后置通知 @After("execution(* com.ren.dao.service.impl.*.*(..))") public void after() { System.out.println("方法执行后。。"); } //环绕通知 @Around("execution(* com.ren.dao.service.impl.*.*(..))") public Object aroud(ProceedingJoinPoint jp) throws Throwable{ System.out.println("环绕前"); System.out.println("签名:"+jp.getSignature());//void com.ren.dao.service.UserService.delete(int) //执行目标方法 Object result = jp.proceed(); System.out.println("环绕后"); return result; }}Spring 配置文件:
<?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"> <!--在 IOC 容器中将切面声明为 Bean 实例 --> <bean id="log1" class="com.ren.log.MyLog"></bean> <!-- 在 Spring IOC 容器中启用 AspectJ 注解支持 --> <aop:aspectj-autoproxy /></beans><aop:config>
元素内部;声明切面:用<aop:aspect>
元素;声明切入点:用<aop:pointcut>
元素: (1)定义在 元素下: 只对当前切面有效; (2)定义在 元素下: 对所有切面都有效; (3)基于 XML 的 AOP 配置不允许在切入点表达式中用名称引用其他切入点;例子: Log.java—切面package com.ren.log;public class Log { public void before() { System.out.println("...方法执行前..."); } public void after() { System.out.println("...方法执行后..."); }}Spring配置文件:beans.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-4.1.xsd"> <!--配置连接点所在程序 --> <bean id="userServiceImpl" class="com.ren.dao.service.impl.UserServiceImpl"></bean> <!--配置日志切面 --> <bean id="log" class="com.ren.log.Log"></bean> <aop:config> <!-- 声明切面 --> <aop:aspect ref="log"> <!-- 声明切入点 --> <aop:pointcut expression="execution(* com.ren.dao.service.impl.*.*(..))" id="pointcut"/> <!--声明通知 --> <aop:before method="before" pointcut-ref="pointcut"/> <aop:before method="after" pointcut-ref="pointcut"/> </aop:aspect> </aop:config></beans>如果有多种切面时也可以如下配置:
<?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="log" class="com.ren.log.Log1"></bean> <bean id="userService" class="com.ren.dao.service.impl.UserServiceImpl"></bean> <bean id="afterLog" class="com.ren.log.AfterLog"></bean> <!-- 1).pointcut:是配置切面位置; 2).advisor:是配置切面的关注点,即是切入什么业务呢; 3).方式一实现: --> <aop:config> <aop:pointcut expression="execution(* com.ren.dao.service.impl.UserServiceImpl.*(..))" id="poincut"/> <aop:pointcut expression="execution(* com.ren.dao.service.impl.*.*(..))" id="poincut"/> <aop:advisor advice-ref="log" pointcut-ref="poincut"/> <aop:advisor advice-ref="afterLog" pointcut-ref="poincut"/> </aop:config> </beans>正常情况下, 基于注解的声明要优先于基于 XML 的声明. 通过 AspectJ 注解, 切面可以与 AspectJ 兼容, 而基于 XML 的配置则是 Spring 专有的. 由于 AspectJ 得到越来越多的 AOP 框架支持, 所以以注解风格编写的切面将会有更多重用的机会.
1. 事务管理定义: 是企业级应用程序开发中必不可少的技术, 用来确保数据的完整性和一致性. 2.事务定义: 事务是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用 3事务的四个关键属性(ACID): (1)原子性(atomicity): 事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用. (2)一致性(consistency): 一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中. (3)隔离性(isolation): 可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来, 防止数据损坏. (4)持久性(durability): 一旦事务完成, 无论发生什么系统错误, 它的结果都不应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中.
先将tx Schema 定义添加到 根元素中去;
<tx:advice>
元素声明事务通知;
声明了事务通知后, 需要将它与切入点关联起来:在 <aop:config>
元素中声明一个增强器通知与切入点关联起来.
设定传播事务属性:<tx:method name="方法名" propagation="传播属性"> 元素
设置;
设置隔离事务属性:<tx:method name="方法名" propagation="传播属性" isolation="事务的隔离级别"> 元素
设置;
. 设置回滚事务属性:
设置超时和只读事务属性:
<tx:annotation-driven>
元素, 并为之用transaction-manager指定事务管理器就可以了.事务传播属性:在 @Transactional 注解的 propagation 属性中定义;设置隔离事务属性:
设置回滚事务属性:
设置超时和只读事务属性:
新闻热点
疑难解答