java深入探究12-框架之Spring

时间:2023-02-03 10:12:16

1.引入Spring

  我们在搭建框架时常常会解决问题:对象创建,对象之间依赖关系如何处理,Spring就是来解决这类问题的:控制反转依赖注入

2.环境搭建

  1)下载源码:其中3.0以下版本源码中有Spring相关所有包【核心包+依赖包】

          3.0以上版本源码中只有spring核心包

  2)导入jar包:spring-framework-3.2.5.RELEASE

    commons-logging-1.1.3.jar           日志

    spring-beans-3.2.5.RELEASE.jar        bean节点

    spring-context-3.2.5.RELEASE.jar       spring上下文节点

    spring-core-3.2.5.RELEASE.jar         spring核心功能

    spring-expression-3.2.5.RELEASE.jar    spring表达式相关表

    以上是必须引入的5个jar文件,在项目中可以用户库管理!

  3)核心配置文件applicationContext.xml 和bean的属性描述   

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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"> </beans>

bean属性描述

/**
* 1) 对象创建: 单例/多例
* scope="singleton", 默认值, 即 默认是单例 【service/dao/工具类】
* scope="prototype", 多例; 【Action对象】
*
* 2) 什么时候创建?
* scope="prototype" 在用到对象的时候,才创建对象。
* scope="singleton" 在启动(容器初始化之前), 就已经创建了bean,且整个应用只有一个。
* 3)是否延迟创建
* lazy-init="false" 默认为false, 不延迟创建,即在启动时候就创建对象
* lazy-init="true" 延迟初始化, 在用到对象的时候才创建对象
* (只对单例有效)
* 4) 创建对象之后,初始化/销毁
* init-method="init_user" 【对应对象的init_user方法,在对象创建爱之后执行 】
* destroy-method="destroy_user" 【在调用容器对象的destriy方法时候执行,(容器用实现类)】
*/
@Test
public void testIOC() throws Exception {
// 得到IOC容器对象 【用实现类,因为要调用销毁的方法】
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_hello/applicationContext.xml");
System.out.println("-----容器创建-----"); // 从容器中获取bean
User user1 = (User) ac.getBean("user");
User user2 = (User) ac.getBean("user"); System.out.println(user1);
System.out.println(user2); // 销毁容器对象
ac.destroy();
}

  4)获取IOC两种方式:

    通过BeanFactory获得IOC容器

// 现在,把对象的创建交给spring的IOC容器
Resource resource = new ClassPathResource("cn/itcast/a_hello/applicationContext.xml");
// 创建容器对象(Bean的工厂), IOC容器 = 工厂类 + applicationContext.xml
BeanFactory factory = new XmlBeanFactory(resource);
// 得到容器创建的对象
User user = (User) factory.getBean("user");

    直接获得IOC容器:ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_hello/applicationContext.xml");

//2. (方便)直接得到IOC容器对象
@Test
public void testAc() throws Exception {
// 得到IOC容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_hello/applicationContext.xml");
// 从容器中获取bean
User user = (User) ac.getBean("user"); System.out.println(user);
}

3.专业术语

  1)侵入式设计:引入框架对现有类结构产生影响struts框架;非侵入式框架:引入框架对现有类结构没有影响

  2)控制反转(Inversion on Control  IOC),依赖注入( dependency injection   DI)

    区别:IOC解决对象创建问题;DI:对象创建完后对对象属性赋值,对象间关系依赖的处理【通过set方法依赖注入】

  3)AOP:面向切面编程:切面简单来说就是由很多重复代码组成,如:事务,日志,权限

4.Spring提供的一站式解决方法

  1) Spring Core  spring的核心功能: IOC容器, 解决对象创建及依赖关系

  2) Spring Web  Spring对web模块的支持。

    1. 可以与struts整合,让struts的action创建交给spring

    2. spring mvc模式

  3) Spring DAO  Spring 对jdbc操作的支持  【JdbcTemplate模板工具类】

  4) Spring ORM  spring对orm的支持:

    1. 既可以与hibernate整合,【session】

    2. 也可以使用spring的对hibernate操作的封装

  5)Spring AOP  切面编程

  6)SpringEE   spring 对javaEE其他模块的支持

5.与applicationContext.xml配置文件有关的配置

  1)配置创建对象的三种方式:

    调用无参数构造器;带参数构造器constructor-arg;工厂创建对象factory-bean(静态方法创建对象,非静态方法创建对象)

<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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"> <!-- ###############对象创建############### --> <!-- . 默认无参数构造器
<bean id="user1" class="cn.itcast.b_create_obj.User"></bean>
--> <!-- . 带参数构造器 -->
<bean id="user2" class="cn.itcast.b_create_obj.User">
<constructor-arg index="" type="int" value=""></constructor-arg>
<constructor-arg index="" type="java.lang.String" value="Jack"></constructor-arg>
</bean> <!-- 定义一个字符串,值是"Jack" ; String s = new String("jack")-->
<bean id="str" class="java.lang.String">
<constructor-arg value="Jacks"></constructor-arg>
</bean>
<bean id="user3" class="cn.itcast.b_create_obj.User">
<constructor-arg index="" type="int" value=""></constructor-arg>
<constructor-arg index="" type="java.lang.String" ref="str"></constructor-arg>
</bean> <!-- . 工厂类创建对象 -->
<!-- # 3.1 工厂类,实例方法 -->
<!-- 先创建工厂 -->
<bean id="factory" class="cn.itcast.b_create_obj.ObjectFactory"></bean>
<!-- 在创建user对象,用factory方的实例方法 -->
<bean id="user4" factory-bean="factory" factory-method="getInstance"></bean> <!-- # 3.2 工厂类: 静态方法 -->
<!--
class 指定的就是工厂类型
factory-method 一定是工厂里面的“静态方法”
-->
<bean id="user" class="cn.itcast.b_create_obj.ObjectFactory" factory-method="getStaticInstance"></bean> </beans>

  2)给对象属性赋值(DI 依赖注入)    

    1.通过构造方法;2.通过set方式;3.p名称空间;4.自动装配;5.注解

    1.通过set方式:bean中加入<property name="userDao" ref="userDao">

     内部bean方式:

<!-- ##############内部bean############## -->
<bean id="userAction" class="cn.itcast.c_property.UserAction">
<property name="userService">
<bean class="cn.itcast.c_property.UserService">
<property name="userDao">
<bean class="cn.itcast.c_property.UserDao"></bean>
</property>
</bean>
</property>
</bean>

    2.p名称空间:p:userDao-ref=""代替了<property>set配置

<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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"> <!-- ###############对象属性赋值############### --> <!--
给对象属性注入值:
# p 名称空间给对象的属性注入值
(spring3.0以上版本才支持)
-->
<bean id="userDao" class="cn.itcast.c_property.UserDao"></bean> <bean id="userService" class="cn.itcast.c_property.UserService" p:userDao-ref="userDao"></bean> <bean id="userAction" class="cn.itcast.c_property.UserAction" p:userService-ref="userService"></bean> <!-- 传统的注入:
<bean id="user" class="cn.itcast.c_property.User" >
<property name="name" value="xxx"></property>
</bean>
-->
<!-- p名称空间优化后 -->
<bean id="user" class="cn.itcast.c_property.User" p:name="Jack0001"></bean> </beans>

    3.自动装配:autowire="byName";autowire="byType"可以在<bean>属性上设置也可以在全局配置在头部default-autowire="byName">   根据名称自动装配(全局)    

<!-- ###############自动装配############### -->
自动去IOC容器中找与属性名同名的引用的对象,并自动注入
<bean id="userDao" class="cn.itcast.d_auto.UserDao"></bean>
<bean id="userService" class="cn.itcast.d_auto.UserService" autowire="byName"></bean>
<!-- 根据“名称”自动装配: userAction注入的属性,会去ioc容器中自动查找与属性同名的对象 -->
<bean id="userAction"
class="cn.itcast.d_auto.UserAction" autowire="byName"></bean> 全局配置
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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" default-autowire="byName"> 根据名称自动装配(全局) <!-- ###############自动装配############### -->
<bean id="userDao" class="cn.itcast.d_auto.UserDao"></bean>
<bean id="userService" class="cn.itcast.d_auto.UserService"></bean>
<bean id="userAction" class="cn.itcast.d_auto.UserAction"></bean>
</beans> 根据类型byType
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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" default-autowire="byType"> <!-- ###############自动装配############### -->
<bean id="userDao" class="cn.itcast.d_auto.UserDao"></bean>
<bean id="userService" class="cn.itcast.d_auto.UserService"></bean> <!-- 如果根据类型自动装配: 必须确保IOC容器中只有一个该类型的对象 -->
<bean id="userAction" class="cn.itcast.d_auto.UserAction"></bean> <!-- 报错: 因为上面已经有一个该类型的对象,且使用了根据类型自动装配
<bean id="userService_test" class="cn.itcast.d_auto.UserService" autowire="byType"></bean>
-->
</beans>

    总结:pring提供的自动装配主要是为了简化配置; 但是不利于后期的维护   

    4.注解:

    使用步骤:

      1.先引入context名称空间

      xmlns:context="http://www.springframework.org/schema/context"

      2.开始扫描:

      <context:component-scan base-package="cn.itcast.e_anno2"></context:component-scan>

      3.使用注解:通过注解的方式,把对象加入ioc容器

            @Component   指定把一个对象加入IOC容器

          @Repository   作用同@Component; 在持久层使用

          @Service      作用同@Component; 在业务逻辑层使用

          @Controller    作用同@Component; 在控制层使用

          @Resource     属性注入

      例子:

bean.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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"> <!-- 开启注解扫描 -->
<context:component-scan base-package="cn.itcast.e_anno2"></context:component-scan> <bean id="userDao" class="cn.itcast.e_anno2.UserDao" scope="prototype">
</bean> </beans> Dao
// 把当前对象加入ioc容器
//@Component("userDao") // 相当于bean.xml 【<bean id=userDao class=".." />】 //@Component // 加入ioc容器的UserDao对象的引用名称, 默认与类名一样, 且第一个字母小写 //@Repository // 在持久层可以选择用这个注解
public class UserDao { public UserDao(){
System.out.println("UserDao.UserDao()");
} public UserDao(int id){
System.out.println("UserDao.UserDao(int id)" + id);
} public void save() {
System.out.println("DB:保存用户!!!");
}
} Service
//@Component("userService") // userService加入ioc容器 //@Component @Service // 表示业务逻辑层的组件
public class UserService { // @Resource // 根据类型查找 【在容器中要确保该类型只有一个变量】 @Resource(name = "userDao") // 根据名称查找
private UserDao userDao; // 去容器中招UserDao类型的变量,找到后就赋值 public void save() {
userDao.save();
}
} Action
//@Component("userAction") // 加入IOC容器 //@Component @Controller // 控制层的组件
public class UserAction { @Resource
private UserService userService; public String execute() {
userService.save();
return null;
}
}

6.Spring与Struts结合

  关键点:struts的action创建交给spring

  导入jar包

  配置xml:Struts.xml;bean.xml;web.xml

    struts.xml:struts路径与action映射配置

    bean.xml:spring ioc 配置

    web.xml:核心过滤器,初始化spring ioc容器

将Spring配置到web.xml中

<!-- 2. spring 配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/bean-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

-----------------------------------------------------------

1.代理模式

  1)理解:代理(Proxy)是一种设计模式,想在目标对象上实现功能扩展可用代理,是用户访问目标对象另一种访问方式

      举例:明星《--经纪人<--用户:另一种方式访问对象给对象添加新功能

  2)静态代理

    1.原理:代理对象实现目标对象一样的接口也可以添加额外功能

    2.优缺点:可以在不改变目标对象功能前提下,对目标对象功能扩展;但是可能会有很多代理的对象,且代理对象和目标对象都要维护

    3.实现方式:与目标对象实现相同接口,在代理对象中添加自己的功能写上目标对象功能--------与装饰者设计模式类似

    4.例子:保存用户(模拟)-》Dao(直接保存)-》DaoProxy(给保存添加事务处理)

IUserDao
package 代理.a_static; public interface IUserDao {
void save();
}
UserDao
package 代理.a_static; public class UserDao implements IUserDao{ @Override
public void save() {
System.out.println("用户保存对象");
} }
UserDaoProxy
package 代理.a_static; public class UserDaoProxy implements IUserDao{
private IUserDao target;
public UserDaoProxy(IUserDao target){
this.target=target;
}
@Override
public void save() {
System.out.println("事务开始");
target.save();
System.out.println("事务结束");
} }
测试APP
package 代理.a_static; public class App {
public static void main(String[] args){
//目标对象
IUserDao target=new UserDao();
//代理对象
IUserDao proxy=new UserDaoProxy(target);
proxy.save();
}
}

  3)动态代理

    1.原理:代理对象不需要实现接口,而是动态的在内存中构建代理对象(需要我们提供代理对象/目标对象 实现接口的类型

    2.核心API:Proxy代理对象工具类

            static Object newProxyInstance(

            ClassLoader loader,       指定当前目标对象使用类加载器

            Class<?>[] interfaces,     目标对象实现的接口的类型

            InvocationHandler h       事件处理器

    3.动态代理总结:代理对象不需要实现接口,但是目标对象必须实现接口,但是遇到目标对象没有实现接口就要用到新技术Cglib代理

    4.例子:

IUserDao
package 代理.b_dynamic; public interface IUserDao {
void save();
}
UserDao
package 代理.b_dynamic; public class UserDao implements IUserDao{ @Override
public void save() {
System.out.println("-------用户已保存-------");
} }
代理对象动态创建,需要提供目标对象
package 代理.b_dynamic; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 给所有的到创建代理对象【动态代理】
*
* 代理对象不需要实现接口,内存内部自己自动生成
* @author Administrator
*
*/
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//内存自动生成的
public Object getProxyInstance(){
return Proxy.newProxyInstance(
//目标对象类加载器
target.getClass().getClassLoader(),
//目标对象实现的接口类型
target.getClass().getInterfaces(),
//核心事务处理器
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("事务开始"); //执行目标对象方法
Object returnValue=method.invoke(target, args);//调用目标对象的方法
System.out.println("事务结束");
return returnValue;
}
}
);
}
}
测试App
package 代理.b_dynamic; public class App {
public static void main(String[] args){
//目的对象
IUserDao target=new UserDao();
//动态代理对象创建
IUserDao proxy=(IUserDao) new ProxyFactory(target).getProxyInstance();
//内存中动态代理对象就是$Proxy0
System.out.println(proxy.getClass()); //执行方法
proxy.save();
} }

  4)Cglib代理:子类代理

    1.原理:在内存中构建一个子类对象从而实现对目标对象功能扩展;

        底层原理:通过小儿快的字节码处理框架ASM,来转换字节码生产新类

    2.引入:普通jdk动态代理必须目标对象有接口实现,当遇到没有接口的就需要用CGLIB了

    3.使用步骤:

      1)引入cglib-jar,spring核心包中有了

      2)开始动态在内存中构建子类

      3)代理的类不能为final,否则报错;目标对象的方法如果为final/staic,那么就不会被拦截,不会执行目标对象额外的业务方法

  总结:在Spring的AOP编程中,如果容器的目标对象有实现接口,用JDK代理,没有实现接口可以用cglib子类代理

    4.例子:

    实现MethodInterceptor接口实现逻辑代码-》写获得子代理对象getProxyInsance()->通过Enhancer工具类赋值父类,创建子类代理对象

UserDao
package h_cglib; public class UserDao {
public void save(){
System.out.println("User保存");
}
}
Proxy
package h_cglib; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy; public class ProxyFactory implements MethodInterceptor{
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//给目标创建代理自对象
public Object getProxyInsance(){
//1.工具类
Enhancer en=new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy arg3) throws Throwable {
// TODO Auto-generated method stub
System.out.println("开始事务。。。");
Object returnValue=method.invoke(target, args);
System.out.println("提交事务。。。");
return returnValue;
} }
测试类
package h_cglib; import g_dynamic.IUserDao; public class App { public static void main(String[] args) {
//目标对象
UserDao target=new UserDao();
System.out.println(target.getClass()); //代理子对象
UserDao proxy=(UserDao) new ProxyFactory(target).getProxyInsance();
proxy.save();
}
}

2.手动实现AOP编程【代码模式】面向切面编程

  1)理解:切片编程,就是将重复代码拿出来放在一个切边类中,之后再和核心逻辑代码组合叫AOP编程

  2)代码例子:都时抽象出重复代码放在AOP切片类中

IUserDao
package e_AOP.myAOP2; public interface IUserDao {
void save();
}
UserDao
package e_AOP.myAOP2; import org.springframework.stereotype.Component; @Component
public class UserDao implements IUserDao{ @Override
public void save() {
System.out.println("User保存");
} }
ProxyFactory
package e_AOP.myAOP2; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class ProxyFactory {
private static Object target;
private static Aop aop; public static Object getProxyInstance(Object target_,Aop aop_){
target=target_;
aop=aop_; return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() { @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
aop.begin();
Object returnValue=method.invoke(target, args);
aop.end();
return returnValue;
}
}); }
}
Aop
package e_AOP.myAOP2; import org.springframework.stereotype.Component;
/**
* 切片代码,与逻辑代码分离
* @author Administrator
*
*/
@Component
public class Aop {
public void begin(){
System.out.println("开始事务。。。");
}
public void end(){
System.out.println("结束事务。。。");
}
}
App
package e_AOP.myAOP2; import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class App {
ApplicationContext ac=new ClassPathXmlApplicationContext("e_AOP/myAOP2/bean.xml");
@Test
public void test(){
IUserDao proxy=(IUserDao) ac.getBean("userDao_proxy");
proxy.save(); }
}
bean.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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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"> <!-- 开启注解扫描 -->
<context:component-scan base-package="e_AOP.myAOP2.ProxyFactory" ></context:component-scan> <!-- 调用工厂类静态方法放回UserDao 代理后的对象 -->
<bean id="userDao_proxy" class="e_AOP.myAOP2.ProxyFactory" factory-method="getProxyInstance">
<constructor-arg index="" ref="userDao"></constructor-arg>
<constructor-arg index="" ref="aop"></constructor-arg>
</bean> </beans>

3.AOP编程

  1)关键字

    AOP: aspect object programming  面向切面编程,关注点代码与业务代码分离!

    关注点:重复代码

    切面:关注点组成的类

    切入点:执行目标对象方法,动态植入切面代码

        可以通过切入点表达式,指定拦截哪些类的哪些方法,给指定的类在运行时候植入切面类代码

  2)步骤

    1.引入aop的jar包  (aspectj aop优秀组件)

      spring-aop-3.2.5.RELEASE.jar   【spring3.2源码】

      aopalliance.jar   【spring2.5源码/lib/aopalliance】

      aspectjweaver.jar   【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】

      aspectjrt.jar   【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】

    2.bean.xml中引入aop名称空间

xmlns:aop="http://www.springframework.org/schema/aop"

http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

    3.开始aop注解

<?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:p="http://www.springframework.org/schema/p"
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/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"> <!-- 开启注解扫描 -->
<context:component-scan base-package="cn.itcast.e_aop_anno"></context:component-scan> <!-- 开启aop注解方式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

    4.使用注解

切入点表达式:返回类型+包名+访问的类+类的方法(* cn.itcast.e_aop_anno.*.*(..))
)

@Aspect                            指定一个类为切面类
@Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") 指定切入点表达式 @Before("pointCut_()") 前置通知: 目标方法之前执行
@After("pointCut_()") 后置通知:目标方法之后执行(始终执行)
@AfterReturning("pointCut_()") 返回后通知: 执行方法结束前执行(异常不执行)
@AfterThrowing("pointCut_()") 异常通知: 出现异常时候执行
@Around("pointCut_()") 环绕通知: 环绕目标方法执行

    5.例子:

package e_AOP.myAOPanno1;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component; @Component
@Aspect //指定当前面为切面类
public class Aop {
//指定切入点表单式:拦截表达式,返回类型+包+哪一类+类方法(有参数可以写参数没有..)
@Pointcut("execution(* e_AOP.myAOPanno1.*.*(..))")
public void pointCut_(){ }
//前置通知:在执行目标方法之前执行
@Before("pointCut_()")
public void begin(){
System.out.println("开始事务/异常");
}
//后置通知:在执行目标方法之后执行
@After("pointCut_()")
public void after(){
System.out.println("提交事务/关闭");
}
//返回后通知:在调用目标方法之后执行
@AfterReturning("pointCut_()")
public void afterRunning(){
System.out.println("afterRunning()");
}
//异常执行
@AfterThrowing("pointCut_()")
public void afterThrowing(){
System.out.println("afterThrowing()");
}
//环绕执行
@Around("pointCut_()")
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕前...");
pjp.proceed();//执行目标方法
System.out.println("环绕后..."); }
}

测试类

package e_AOP.myAOPanno1;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class App {
ApplicationContext ac =
new ClassPathXmlApplicationContext("e_AOP/myAOPanno1/bean.xml");
// 目标对象有实现接口,spring会自动选择“JDK代理”
@Test
public void testApp() {
IUserDao userDao = (IUserDao) ac.getBean("userDao");
System.out.println(userDao.getClass());//$Proxy001
userDao.save();
}
//目标对象没有实现接口,spring会用“cglib代理”
@Test
public void testCglib(){
OrderDao orderDao=(OrderDao) ac.getBean("orderDao");
System.out.println(orderDao.getClass());
orderDao.save();
}
}

全部配置在bean.xml中

<!-- 配置OrderDao -->
<bean id="userDao" class="e_AOP.myAOXml.UserDao"></bean>
<!-- 配置UserDao -->
<bean id="orderDao" class="e_AOP.myAOXml.OrderDao"></bean>
<!-- 配置Aop -->
<bean id="aop" class="e_AOP.myAOXml.Aop"></bean>
<!-- Aop配置-->
<aop:config>
<!-- 定义一个切入点表达式:拦截哪些方法 -->
<aop:pointcut expression="execution(* e_AOP.myAOXml.*.*(..))" id="pt"/>
<!-- 切面 -->
<aop:aspect ref="aop">
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pt"/>
<!-- 前置通知: 在目标方法调用前执行 -->
<aop:before method="begin" pointcut-ref="pt"/>
<!-- 后置通知: -->
<aop:after method="after" pointcut-ref="pt"/>
<!-- 返回后通知 -->
<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
<!-- 异常通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>

切面类的所有配置

<!-- 切面类 -->
<bean id="aop" class="cn.itcast.g_pointcut.Aop"></bean> <!-- Aop配置 -->
<aop:config> <!-- 定义一个切入点表达式: 拦截哪些方法 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.*.*(..))" id="pt"/>--> <!-- 【拦截所有public方法】 -->
<!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>--> <!-- 【拦截所有save开头的方法 】 -->
<!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>--> <!-- 【拦截指定类的指定方法, 拦截时候一定要定位到方法】 -->
<!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>--> <!-- 【拦截指定类的所有方法】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>--> <!-- 【拦截指定包,以及其自包下所有类的所有方法】 -->
<!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>--> <!-- 【多个表达式】 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!-- 下面2个且关系的,没有意义 -->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) &amp;&amp; execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>--> <!-- 【取非值】 -->
<!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
<aop:pointcut expression=" not execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/> <!-- 切面 -->
<aop:aspect ref="aop">
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>

4.Spring DAO

  类似于DbUtil

  1)配置xml导入JdbcTemplate对象

<!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///hib_demo"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxStatements" value="100"></property>
<property name="acquireIncrement" value="2"></property>
</bean> <!-- 2. 创建JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean> <!-- dao 实例 -->
<bean id="userDao" class="cn.itcast.h_jdbc.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>

  2)使用这个JdbcTemplate对象

private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
} public void save() {
String sql = "insert into t_dept(deptName) values('test');";
jdbcTemplate.update(sql);
} public Dept findById(int id) {
String sql = "select * from t_dept where deptId=?";
List<Dept> list = jdbcTemplate.query(sql,new MyResult(), id);
return (list!=null && list.size()>0) ? list.get(0) : null;
} public List<Dept> getAll() {
String sql = "select * from t_dept";
List<Dept> list = jdbcTemplate.query(sql, new MyResult());
return list;
} class MyResult implements RowMapper<Dept>{ // 如何封装一行记录
@Override
public Dept mapRow(ResultSet rs, int index) throws SQLException {
Dept dept = new Dept();
dept.setDeptId(rs.getInt("deptId"));
dept.setDeptName(rs.getString("deptName"));
return dept;
} }

5.Spring事务管理

    1).事务管理分类

      1.编程式事务管理:是jdbc中conn.setAutoCommite(false)

               Hibernate中是session.beginTransaction()

               细粒度的事务控制

      2.声明式事务管理:只需在配置文件中配置,实现了对事务控制最大解耦

               Jdbc:DataSourceTransactionManager

                Hibernate技术:HibernateTransactionManager

                粗粒度事务控制:只能给整个方法应用事务,因为aop拦截的是方法

    2)配置事务管理

      1.引入spring-aop相关4个jar文件;2.引入aop名称空间3.引入tx名称空间

      2.spring声明式事务管理配置:

          1)配置事务管理器类2)配置事务增强如何管理事务3)AOP配置拦截哪些方法应用上面的增强

<!-- #############5. Spring声明式事务管理配置############### -->
<!-- 5.1 配置事务管理器类 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean> <!-- 5.2 配置事务增强(如果管理事务?) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" read-only="false"/>
</tx:attributes>
</tx:advice> <!-- 5.3 Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 -->
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.a_tx.DeptService.*())" id="pt"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>

    3)完整例子

1. DeptDao.java
public class DeptDao { // 容器注入JdbcTemplate对象
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
} public void save(Dept dept){
String sql = "insert into t_dept (deptName) values(?)";
jdbcTemplate.update(sql,dept.getDeptName());
}
} 2. DeptService
public class DeptService { // 容器注入dao对象
private DeptDao deptDao;
public void setDeptDao(DeptDao deptDao) {
this.deptDao = deptDao;
} /*
* 事务控制?
*/
public void save(Dept dept){
// 第一次调用
deptDao.save(dept); int i = 1/0; // 异常: 整个Service.save()执行成功的要回滚 // 第二次调用
deptDao.save(dept);
}
}
3. App 测试类
@Test
public void testApp() throws Exception {
//容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_tx/bean.xml"); // 模拟数据
Dept dept = new Dept();
dept.setDeptName("测试: 开发部"); DeptService deptService = (DeptService) ac.getBean("deptService");
deptService.save(dept); }
4. bean.xml (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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 1. 数据源对象: C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///hib_demo"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxStatements" value="100"></property>
<property name="acquireIncrement" value="2"></property>
</bean> <!-- 2. JdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean> <!-- 3. dao实例 -->
<bean id="deptDao" class="cn.itcast.a_tx.DeptDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean> <!-- 4. service实例 -->
<bean id="deptService" class="cn.itcast.a_tx.DeptService">
<property name="deptDao" ref="deptDao"></property>
</bean> <!-- #############5. Spring声明式事务管理配置############### -->
<!-- 5.1 配置事务管理器类 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean> <!-- 5.2 配置事务增强(如果管理事务?) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="*" read-only="false"/>
</tx:attributes>
</tx:advice> <!-- 5.3 Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 -->
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.a_tx.DeptService.*())" id="pt"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config> </beans>

       3.注解方式实现事务管理

注解声明讲解:

事物传播行为介绍:
@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务. 事物超时设置:
@Transactional(timeout=) //默认是30秒 事务隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED)
读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ)
可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE)
串行化 MYSQL: 默认为REPEATABLE_READ级别
SQLSERVER: 默认为READ_COMMITTED 脏读 : 一个事务读取到另一事务未提交的更新数据
不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说,
后续读取可以读到另一事务已提交的更新数据. 相反, "可重复读"在同一事务中多次
读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据
幻读 : 一个事务读到另一个事务已提交的insert数据 @Transactional注解中常用参数说明 参 数 名 称 功 能 描 述 readOnly 该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true) rollbackFor 该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如: 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) 续表) 参 数 名 称 功 能 描 述 rollbackForClassName 该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如: 指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException") 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"}) noRollbackFor 该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如: 指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) noRollbackForClassName 该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如: 指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException") 指定多个异常类名称: @Transactional(noRollbackForClassName={"RuntimeException","Exception"}) propagation 该属性用于设置事务的传播行为,具体取值可参考表6-。 例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) isolation 该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置 timeout 该属性用于设置事务的超时秒数,默认值为-1表示永不超时 注意的几点:
@Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能. 2用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
如下:
@Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚
public void methodName() {
throw new Exception("注释"); }
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException("注释");)会回滚
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("注释");
} 、@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。 、@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 <tx:annotation-driven/>元素的出现 开启 了事务行为。 、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因 此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。

配置文件关于事务管理部分的改为

<!-- 事务管理器类 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean> <!-- 开启注解扫描 -->
<context:component-scan base-package="cn.itcast.b_anno"></context:component-scan> <!-- 注解方式实现事务: 指定注解方式实现事务 -->
<tx:annotation-driven transaction-manager="txManager"/>

调用事务的方法使用注解的用法

@Transactional(
readOnly=false,
timeout=-1,
isolation=Isolation.DEFAULT,
propagation=Propagation.REQUIRED
)
public void save(Dept dept){
//第一次调用
deptDao.save(dept); int i = 1/0; // 异常: 整个Service.save()执行成功的要回滚 // 第二次调用
deptDao.save(dept);
}

      4.事务属性

@Transactional(
readOnly = false, // 读写事务
timeout = -1, // 事务的超时时间不限制
noRollbackFor = ArithmeticException.class, // 遇到数学异常不回滚
isolation = Isolation.DEFAULT, // 事务的隔离级别,数据库的默认
propagation = Propagation.REQUIRED // 事务的传播行为
)
public void save(Dept dept){
deptDao.save(dept);
int i = 1/0;
deptDao.save(dept);
} 事务传播行为:
Propagation.REQUIRED
指定当前的方法必须在事务的环境下执行;
如果当前运行的方法,已经存在事务, 就会加入当前的事务;
Propagation.REQUIRED_NEW
指定当前的方法必须在事务的环境下执行;
如果当前运行的方法,已经存在事务: 事务会挂起; 会始终开启一个新的事务,执行完后; 刚才挂起的事务才继续运行