书接上文!!
11,异常通知(schem-base方式下)
- 新建一个类实现ThrowsAdvice接口
1.1必须自己写方法,且方法名必须叫afterThrowing
1.2有两种参数方式
1.2.1必须是1个或4个
1.3异常与切点报的切点的异常一致
package com.mywolf.advice;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import javax.servlet.ServletException;
import org.springframework.aop.ThrowsAdvice;
public class MyThrow implements ThrowsAdvice{
// public void afterThrowing(Exception ex) throws Throwable {
// System.out.println("执行异常通知通过schema-base方式");
// }
public void afterThrowing(Method m, Object[] args, Object target, Exception ex) {
// Do something with all arguments
System.out.println("执行异常通知通过schema-base方式");
}
}
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: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="mythrow" class="com.mywolf.advice.MyThrow"></bean>
<aop:config>
<aop:pointcut
expression="execution(* com.mywolf.test.Demo.demo01())"id="mypoint" />
<aop:advisor advice-ref="mythrow" pointcut-ref="mypoint" />
</aop:config>
<bean id="demo" class="com.mywolf.test.Demo"></bean>
</beans>
12.环绕通知
1.环绕通知的概念:把前置通知和后置通知都写在同一个通知里就构成了环绕通知。
实现步骤:
编写一个类,实现MethodInterceptor接口
public class MyArround implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("环绕-前置");
Object proceed = arg0.proceed();//方行,调用切点方法
System.out.println("环绕-后置");
return proceed;
}
}
配置applicationContext.xml
<bean id="demo" class="com.mywolf.test.Demo"></bean>
<bean id="myarround" class="com.mywolf.advice.MyArround"></bean>
<aop:config>
<aop:pointcut
expression="execution(* com.mywolf.test.Demo.demo01())" id="mypoint" />
<aop:advisor advice-ref="myarround" pointcut-ref="mypoint" />
</aop:config>
</beans>
13.关于aspectj方式:
新建一个类,类中方法名任意
package com.mywolf.advice;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvice {
public void mybefore(String name1, int age1) {
// 前置通知
System.out.println("前置通知" + name1);
}
public void mybefore1(String name1) {
// 前置通知
System.out.println("前置通知" + name1);
}
public void myafter() {
// 后置通知
System.out.println("后置通知1");
}
public void myafterning() {
// 后置通知
System.out.println("后置通知2");
}
public void mythrow() {
// 后置通知
System.out.println("异常");
}
public Object myarround(ProceedingJoinPoint p) throws Throwable {
// 环绕通知
System.out.println("环绕-前置");
Object proceed = p.proceed();
System.out.println("环绕-后置");
return proceed;
}
}
配置applicationContext.xml配置文件
<bean id="demo" class="com.mywolf.test.Demo"></bean>
<bean id="myadvice" class="com.mywolf.advice.MyAdvice"></bean>
<!-- 使用aspect方式完成各种通知 -->
<!-- <aop:config> <aop:pointcut expression="execution(* com.mywolf.test.Demo.demo01())"
id="mypoint" /> <aop:advisor advice-ref="myarround" pointcut-ref="mypoint"
/> </aop:config> -->
<aop:config>
<aop:aspect ref="myadvice">
<aop:pointcut
expression="execution(* com.mywolf.test.Demo.demo01(String,int)) and args(name1,age1)" id="mypoint" />
<aop:pointcut
expression="execution(* com.mywolf.test.Demo.demo01(String)) and args(name1)" id="mypoint1" />
<aop:before method="mybefore" pointcut-ref="mypoint" arg-names="name1,age1"/>
<aop:before method="mybefore1" pointcut-ref="mypoint1" arg-names="name1"/>
<!-- <aop:after method="myafter" pointcut-ref="mypoint"/>
<aop:around method="myarround" pointcut-ref="mypoint"/> -->
</aop:aspect>
</aop:config>
解释:
1.<aop:after:后置通知,不管是否出现异常都会执行的通知
2.<aop:after-returning method=""/>:也是后置通知,只有切点没有异常正常执行的时候才会执行
3. <aop:after-throwing method=""/>和<aop:after:和<aop:after-returning method=""/>的执行顺序与他们在配置文件中的顺序有关。
4.特别注意红字部分的写法以及他们参数的写法都要保持对应(位置和名字都必须对应)
13.使用注解(基于 Aspect)
1.spring不会自动去寻找注解,必须告诉spring哪些包下的类中可能有注解
1.1引入xmlns:context=http://www.springframework.org/schema/context
并添加 http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<!—组键扫描 -->
<context:component-scan base-package="com.mywolf.advice"></context:component-scan>
若扫描多个包则用逗号隔开
<context:component-scan base-package="com.mywolf.advice,com.mywolf.test">
</context:component-scan>
2.@Component
2.1相当于<bean />
2.1如果没有参数把类名首字母变小写,相当于<bean id=””>
2.3@Component(“自定义名称”)
3.实现步骤
3.1在spring配置文件中,设置注解在哪个包中
<!--组键扫描 -->
<context:component-scan base-package="com.mywolf.advice ,com.mywolf.test">
</context:component-scan>
3.2对于有切点的类
package com.mywolf.test;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
public class Demo {
//定义切点
@Pointcut("execution(* com.mywolf.test.Demo.demo01())")
public void demo01() {
// int i= 5/0;
System.out.println("demo01");
}
}
对于含有通知的类
package com.mywolf.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAdvice {
@Before("com.mywolf.test.Demo.demo01()")
public void mybefore() {
System.out.println("前置");
}
}
最后使用cglib代理(在配置文件中)
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
16.动态代理
1.为了解决静态代理频繁编写代理功能的缺点
2.动态代理的分类
2.1jdk动态代理
2.2cglib动态代理
3.JDK动态代理
1.和cglib动态代理相比
1.1优点:jdk自带,不需要额外导入jar
1.2缺点
1.2.1真实对象必须实现接口
1.2.2利用反射机制,效率不高
4.cglib动态代理
1.cglib优点:
1.1基于字节码文件(编译后的.class文件),生成真实对象的子类
1.2运行效率高于JDK动态代理
1.3不需要实现接口
5.使用spring-aop时,只要出现Proxy和真实对象转换异常
5.1设置为true使用cglib
5.2设置false使用jdk(默认值)
<aop:aspectj-autoproxy proxy-target-class=“true”></aop:aspectj-autoproxy>
17.自动注入
1.在spring配置文件中对象名和ref=“id” id名相同使用自动注入,可以不配置
2.两种配置办法
2.1在中通过autowire=“”配置,只对这个生效
3. autowire可取值
3.1default:默认值,根据全局default-autowire=‘’‘’值,默认全局和局部都没有配置的情况写就是no,不使用自动注入:
3.2no:不自动注入
3.3byName:通过名称自动注入,在spring容器中找类的id
3.4byType:根据类型注入
3.4.1spring容器中不可以出现两个相同类型的
3.5constructor:根据构造方法注入
3.5.1提供对应参数的构造方法(构造方法参数中包含注入那个注入对象)
3.5.2底层使用byName构造方法参数名和其他的id相同
18.spring中加载properties属性文件:
1.在src下新建xxx.properties属性文件
2.在spring配置文件中先引入xmls:context 对象命名空间和属性命名空间
2.1如果需要加载多个配置文件就用逗号分隔(其中classpath:表示src文件下)
<context:property-placeholder location=“classpath:db.properties”/>
3.添加了属性文件加载,并且在中开启了自动注入
3.1注意扫描的配置文件的写法特别注意红字部分的写法,不能再用sqlSessionFactory
和ref 而要用name=“sqlSessionFactoryBeanName” value=“factory” 用ref不行的原因在于对象在创建的时候还没有加载属性文件对对象的属性赋值,导致出错
<!-- 扫描器相当于mybatis.xml中mappers下的package标签 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 要扫描哪个包 -->
<property name="basePackage" value="com.mywolf.mapper"></property>
<!-- 和factory产生关系 -->
<property name="sqlSessionFactoryBeanName" value="factory"></property>
</bean>
4.在被spring管理的类中通过@Value(“${key}”)取出properties(属性文件中的值)
4.1使用注解要在配置文件中添加注解扫描
<context:component-scan base-package="包名"></context:component-scan>
4.2在类中添加
4.2.1key和变量名可以不同
4.2.2变量类型任意,只要保证key对应的value能转换成这个类型就行(也就是属性文件中的属性值应该能够赋值给要赋值的变量)
@Value("${key}")
private String test;
19scope属性
1.的属性
2.作用:控制对象的有效范围(单例(全程只有一个对象)、多例(全程可以有多个对象))
3.标签对应的对象默认是单例的
3.1也就是在默认情况下使用标签由spring创建对象时一个标签对应一个对象,无论从spring容器中取多少次(同一个标签创建对应的对象)都是同一个对象。
4.scope可取值
4.1singleton :默认值,单例
4.2 prototype 多例,即每次取同一个标签的对象都会重新创建对象
4.3request 每次request请求都会重新创建一个对象(使用springMVC框架中用到)
4.4session 每个会话对象内,对象是单例的
4.5application 在application对象内是单例
4.6global session spring 推出 的一个对象,依赖于spring-webmvc-portlet,类似于session
5.单例模式(保证在程序运行过程中只有一个对象)
1.作用:在应用程序中保证最多只能有一个实例
2.优点:
2.1提升运行效率
2.2 实现数据共享 案例:application对象
3懒汉式
3.1对象只有被调用时才去创建
3.2实例代码
package com.mywolf.single;
/**
*
* @author huihui
*懒汉式单例设计模式
*/
public class SingleTon {
private static SingleTon singleton;
/**
* 不让其通过new来创建重复的对象
*
*/
private SingleTon(){
}
public static SingleTon getSingleTon() {
//添加逻辑,如果已经实例化过就直接返回
if(singleton==null) {
/**
* 多线程的情况下,可能出现多个线程同时访问这个if因此加上锁
*/
synchronized(SingleTon.class) {
/**
* 双重验证
*/
if(singleton==null) {
singleton=new SingleTon();
return singleton;
}
}
}
return singleton;
}
}
Test
package com.mywolf.single;
public class Test {
public static void main(String[] args) {
SingleTon singleton1=SingleTon.getSingleTon();
SingleTon singleton2=SingleTon.getSingleTon();
System.out.println(singleton1==singleton2);
}
}
3.3缺点:由于添加了锁导致效率低
4.饿汉式
4.1解决了懒汉式中多线程访问可能出现同一个对象效率低的问题
package com.mywolf.single;
/**
*
* @author huihui
*饿汉式单例设计模式(当这个类被加载的时候就立马创建对象)
*/
public class SingleTon {
private static SingleTon singleton=new SingleTon();
/**
* 不让其通过new来创建重复的对象
*
*/
private SingleTon(){
}
public SingleTon getSingleTon() {
return singleton;
}
}
20.声明式事务
1.编程式事务
1.1由程序员编程事务控制代码
1.2OpenSessionInView 编程式事务
2.声明式事务
2.1事务控制代码已经由spring写好,程序员只需要声明出哪些地方需要进行事务控制和如何进行事务控制
3.声明式事务都是针对于ServiceImpl类下的方法(service里面写的就是业务(一个业务对应一个事务))
4.事务管理器是基于通知的(advice)
<!-- 声明式事务 -->
<!-- 数据源 -->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSouce" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"></property>
<property name="url" value="${jdbc.url} "></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 第二步配置声明式事务 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSouce"></property><!-- 事务是要提交给数据库的因此要跟数据库连起来 -->
</bean><!-- 这是一个事务管理器 -->
<!-- 配置声明式事务 -->
<tx:advice id="txadvice" transaction-manager="txManager">
<tx:attributes>
<!-- 哪些方法需要有事务控制 -->
<!-- 也支持通配 -->
<!-- <tx:method name="insert"/> -->
<tx:method name="ins*"/><!-- 就是表示以ins开头的所有方法 -->
<tx:method name="upd*"/><!-- 就是表示以upd开头的所有方法 -->
<tx:method name="del*"/><!-- 就是表示以del开头的所有方法 -->
</tx:attributes>
</tx:advice>
<!-- 第一步还是配置切点 -->
<aop:config>
<aop:pointcut expression="execution(* com.mywolf.service.impl.*.*(..))" id="mypoint"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="mypoint"/>
</aop:config>
5声明式事务属性解释(<tx:method name=“ins*”/>后的属性)
-
name=“”属性:哪些方法需要有事务控制
1.1支持*通配符
2.readonly=“booblean”是否是只读事务
2.1如果为true,告诉数据库此事务为只读事务,数据库优化,会对性能有一定提升,所以只要是查询的方法,建议使用此数据。
2.2如果为false(默认值),事务需要提交的事务,建议新增,删除,修改
3.propagation 控制事务传播行为
3.1当一个具有事务控制的方法被另一个有事务控制的方法调用后,需要如何管理事务(新建事务?在事务中执行?把事务挂起?报异常?)
3.2默认值:REQUIRED如果当前有事务,就在事务中执行,如果当前没有事务,新建一个事务。
3.3 SUPPORTS: 如果当前有事务,就在事务中执行,如果当前没有事务,就在非事务状态下执行
3.4 MANDATORY:必须在事务内部执行,如果当前有事务,就在事务中执行,如果没有事务,报错
3.5 REQUIRES_NEW:必须在事务中执行,如果当前没有事务,新建事务,如果当前有事务,把当前事务挂起。
3.6 NOT_SUPPORTED:必须在非事务下执行,如果当前没有事务,正常执行,如果当前有事务,把当前事务挂起
3.7 NEVER:必须在非事务状态下执行,如果当前没有事务,正常执行,如果当前有事务,报错
3.8. NESTED:必须在事务状态下执行,如果当前没有事务,新建事务,如果当前有事务,创建一个嵌套事务。 -
isolation=“”事务隔离级别
4.1在多线程或并发访问下如何保证访问到的数据具有完整性的(数据是对的)
4.2脏读
4.2.1一个事务(事务A)读取到另一个事务(事务B)中未提交的数据,另一个事务(事务B)中的数据可能发生了改变,此时A事务读取的事务可能和数据库中的数据不一致,此时认为数据是脏数据,读取数据过程叫脏读。
4.3不可重复读
4.3.1主要针对的是某行数据(或行中某列)
4.3.2主要针对的操作是修改操作
4.3.3两次读取在同一个事务内
4.3.4当事务A第一次读取事务之后,事务B对事务A读取的数据进行修改,事务A中再次读取的数据和之前读取的数据不一致,过程不可重复。
4.4幻读
4.4.1主要针对的操作是新增或删除
4.4.2两次事务的结果
4.4.3事务A按照特定条件查询出结果,事务B新增了一条符合条件的数据。事务A中查询的数据和数据库中的数据不一致,事务A好像出现了幻觉,这种情况称为幻读。 -
isolation可取值:
- DEFAULT:默认值,由底层数据库自行判断应该使用什么隔离级别
- READ_UNCOMMITTED:可以读取未提交数据,可能出现脏读、不可重复读、幻读
(但是他的效率最高)
3.READ_COMMITTED:只能读取其他事务已经提交的数据,可以防止脏读,可能出现不可重复读和幻读。
-
REPEATABLE_READ:读取的数据被添加锁,防止其他事务修改此数据,可以防止不可重复读和脏读,可能出现幻读
-
SERIALIZABLE:排队操作,对整个表添加锁,一个事务在操作数据时,另一个事务等待这个事务操作完成后才能操作这个表。(最安全,效率最低)
6.事务回滚rollback-for=“异常类型的全限定路径(如java.lang.Exception)”
6.1当出现什么异常时需要进行回滚
6.2建议:给定该属性值
6.2.1手动抛异常一定要给该属性值(手动抛异常:throw new Exception) -
no-rollback-for=""当出现什么异常时不回滚事务。
21.spring中常用注解
1 @Component 创建对象,相当于配置
2 @Service 与@Component 功能相同
写在serviceImpl类上
3 @Repository 与@Component 功能相同
写在数据访问层类上
4 @Controller 与@Component 功能相同
写在控制器类上
5 @Resource 和 @Autowired 的区别
@Resource 不需要写对象的get /set @Autowired不需要写对象的get /set
Java中的注解 Spring中的注解
默认按照byName注入,如果没有名称对象
按照byType注入 默认按照byType注入
7.@Value()获取 properties(属性文件)中的内容
8.@pointcut()定义切点
9.Aspect()定义切面类
10.@Before()前置通知
11.@After()后置通知
12.@AfterReturning 后置通知,必须切点正确执行
13@AfterThrowing 异常通知
14@Arround环绕通知