spring

spring

一个大容器,用来管理项目中的所有对象
spring不排斥其他框架,帮其他框架管理对象

Spring中的主要组件如图:
在这里插入图片描述
Core Container是spring框架的核心控制器,有四个部分,核心组件是其中的Beans、Core和Context组件。

  • Beans组件可以说是比较核心的,Spring框架将对象之间的依赖关系转用配置文件、注解来配置,注入关系通过IOC容器来管理,而IOC容器中管理的实体就是被Bean包裹的对象。Bean就是被包装之后的Java对象,由Spring框架创建和维护。

  • Context组件是Bean的上下文,是Bean的生存环境,用于建立和维护Bean之间的关系,所以说Context其实是Bean关系的集合,这个关系的集合就叫做IOC容器。

  • Core组件,就是发现、建立、维护Bean之间的关系所需要的一系列的工具类(Util)。

spring搭建

1.导包

(1)基础包,即Core Container的四个包
在这里插入图片描述
(2)日志包
在这里插入图片描述

2.创建一个对象
public class User {
	private String name;
	private Integer age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}

}
3.书写配置注册对象到容器

配置文件名任意,位置任意(一般叫applicationContext.xml,放在src目录下)

第一步:导入约束

(1)window–preferences–xml catalog–add–
在这里插入图片描述
(2)applicationContext.xml写好根元素

<?xml version="1.0" encoding="UTF-8"?>
<beans></beans>

(3)applicationContext.xml切换到design视图
选中beans右键edit namespaces–add–
在这里插入图片描述
(4)再add–
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(5)导入约束后applicationContext.xml中自动变成

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd ">
</beans>
第二步:将对象交给spring管理

在这里插入图片描述

4.测试
package com.xdh.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.xdh.bean.User;

public class HelloDemo {
	@Test
	public void fun1(){
		//1 创建容器对象
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		//2 向容器"要"user对象
		User u = (User) ac.getBean("user");
		//3 打印user对象
		System.out.println(u);
		
	}

}

IOC和DI

IOC:Inverse of control:反转控制
以前对象的创建以及依赖关系的注入都是由开发人员自己维护,使用了spring之后,对象的创建和依赖关系的注入都可以由spring完成。反转控制就是反转了对象的创建方式,从我们自己创建反转给了程序(spring)

DI:dependency injection:依赖注入
实现IOC思想需要DI做支持
注入方式:

  • set注入
  • 构造方法注入
  • 字段注入
    注入类型:
    1)值类型注入 --8大基本数据类型
    2)引用类型注入 --将依赖对象注入

BeanFactory和ApplicationContext

BeanFactory:
spring原始接口,功能较为单一
BeanFactory接口实现类的容器,每次在获得对象时才会创建对象

ApplicationContext:
每次容器启动时就会创建容器中配置的所有对象,并提供更多功能
加载配置文件方式:

  • 从类路径下加载配置文件:ClassPathXmlApplicationContext
  • 从硬盘绝对路径下加载配置文件:FileSystemXmlApplicatinContext(d:/xxx/xxx.xml)

总结:web开发中,使用ApplicationContext,在资源匮乏的环境可以使用BeanFactory

配置详解

1.bean元素

	<!-- 将User对象交给spring容器管理 -->
	<!-- Bean元素:使用该元素描述需要spring容器管理的对象
			class属性:被管理对象的完整类名.
			name属性:给被管理的对象起个名字,获得对象时根据该名称获得对象.  
					可以重复.可以使用特殊字符.
			id属性: 与name属性一模一样. 
					名称不可重复,不能使用特殊字符.
			小结: 尽量使用name属性.
			scope属性:
				singleton(默认值):单例对象,被标识为单例的对象在spring容器中只会存在一个实例
				prototype:多例,标识为多例的对象,每次在获得时才会创建对象,每次创建都是新的对象
				request(了解):web环境下,对象与request生命周期一致
				session(了解):web环境下,对象与session生命周期一致
			小结:一般使用singleton
				spring整合struts2时,把Action交给spring框架管理,每访问一次Action要创建一个Action对象,	  		所以配成多例
			生命周期属性:
				配置一个方法作为生命周期初始化方法,spring会在对象创建之后立即调用
					init-method=""
				配置一个方法作为生命周期的销毁方法,spring容器在关闭并销毁容器中的对象之前调用
					destroy-method=""
	  -->
	<bean name="user" class="com.xdh.bean.User" scope="singleton" init-method="init" destroy-method="destroy" ></bean>

2.三种对象创建方式

<!-- 创建方式1:空参构造创建(常用)  -->
	<bean name="user" class="com.xdh.bean.User" ></bean>
<!-- 创建方式2:静态工厂创建 (了解)
		  调用UserFactory的createUser方法创建名为user2的对象,放入容器
		 createUser是静态方法,直接类名.方法名
	 -->
	<bean  name="user2" 
		class="com.xdh.createbean.UserFactory" 
		factory-method="createUser" ></bean>
<!-- 创建方式3:实例工厂创建 (了解)
		 调用UserFactory对象的createUser2方法创建名为user3的对象,放入容器
		createUser2是普通方法,需先创建UserFactory实例,再调方法
	 -->
	<bean  name="user3" 
		factory-bean="userFactory"
		factory-method="createUser2" ></bean>
		
	<bean  name="userFactory" 
		class="com.xdh.createbean.UserFactory"   ></bean>
public class UserFactory {
	
	public static User createUser(){
			
			System.out.println("静态工厂创建User");
			
			return new User();
			
		}
		
		public  User createUser2(){
			
			System.out.println("实例工厂创建User");
			
			return new User();
			
		}

}

方式一是spring创建对象,方式二三是手动创建对象,spring调创建对象的方法

3.spring的分模块配置

即引入别的spring配置文件

<!-- 导入其他spring配置文件 -->
<import resource="com/xdh/createdemo/applicationContext.xml"/>

4.spring属性注入

(1).set方法注入(最主要)

在这里插入图片描述

(2).构造函数注入
<bean name="user2" class="com.xdh.bean.User" >
	<!-- name属性: 构造函数的参数名 -->
	<!-- index属性: 构造函数的参数索引 -->
	<!-- type属性: 构造函数的参数类型-->
	<constructor-arg name="name" index="0" type="java.lang.Integer" value="999"  ></constructor-arg>
	<constructor-arg name="car" ref="car" index="1" ></constructor-arg>
</bean>
(3).p名称空间注入(了解)

在使用p名称空间注入时要先引入P名称空间:
在这里插入图片描述

<!-- p名称空间注入, 走set方法
	1.导入P名称空间  xmlns:p="http://www.springframework.org/schema/p"
	2.使用p:属性完成注入
		|-值类型: p:属性名="值"
		|-对象类型: p:属性名-ref="bean名称"
 -->
	<bean  name="user3" class="com.xdh.bean.User" p:name="jack" p:age="20" p:car-ref="car"  >
	</bean>
(4).spel注入(了解)

spel:spring expression language(spring表达式语言),不支持引用类型的注入

<bean  name="user4" class="com.xdh.bean.User" >
		<property name="name" value="#{user.name}" ></property>
		<property name="age" value="#{user3.age}" ></property>
		<property name="car" ref="car" ></property>
</bean>
(5).复杂类型的注入

例如集合、数组

public class CollectionBean {
	private Object[] arr;//数组类型注入
	private List list;//list/set 类型注入
	private Map map;//map类型注入
	private Properties prop;//properties类型注入
	...
}
	
<!-- 复杂类型注入 -->
<bean name="cb" class="com.xdh.bean.CollectionBean" >
	<!-- array注入 -->
	<!-- 如果数组中只准备注入一个值(对象),直接使用value|ref即可 
	<property name="arr" value="tom" ></property>
	-->
	<!-- 多个元素注入 -->
	<property name="arr">
		<array>
			<value>tom</value>
			<value>jerry</value>
			<ref bean="user4" />
		</array>
	</property>
	
	<!-- 如果List中只准备注入一个值(对象),直接使用value|ref即可 
	<property name="list" value="jack" ></property>-->
	<property name="list"  >
		<list>
			<value>jack</value>
			<value>rose</value>
			<ref bean="user3" />
		</list>
	</property>
	<!-- map类型注入 -->
	<property name="map"  >
		<map>
			<entry key="url" value="jdbc:mysql:///crm" ></entry>
			<entry key="user" value-ref="user4"  ></entry>
			<entry key-ref="user3" value-ref="user2"  ></entry>
		</map> 
	</property>
	<!-- prperties 类型注入 -->
	<property name="prop"  >
		<props>
			<prop key="driverClass">com.jdbc.mysql.Driver</prop>
			<prop key="userName">root</prop>
			<prop key="password">1234</prop>
		</props>
	</property>
</bean>

补充

1、spring在web项目中的生命周期

spring是一个存放了大量对象的容器,所以不可能每访问Action中的一个方法,就创建一个ApplicationContext容器对象,即不能通过

//1 创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2 向容器"要"user对象
User u = (User) ac.getBean("user");

创建容器对象,ApplicationContext容器对象在整个项目中只创建和销毁一次。

ServletContextListner监听器,用来监听ServletContext的创建与销毁,spring内置了ContextLoaderListener监听器,底层也是ServletContextListner,ServletContextListner监听器创建好后,把容器存在ServletContext中,可以让spring容器随项目的启动而创建,随项目的关闭而销毁。

监听器:监听web服务器的运行,当发生特定的事件时采取预先设定的处理措施的组件

public class ContextLoaderListener extends ContextLoader implements ServletContextListener
  • 配置ContextLoaderListener监听器需先导包:
    在这里插入图片描述
  • 再配置:
  <!-- 可以让spring容器随项目的启动而创建,随项目的关闭而销毁 -->
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  • 获得容器,再从容器里拿对象
	//获得spring容器=>从Application域获得即可

	//1 获得servletContext对象
	ServletContext sc = ServletActionContext.getServletContext();
	//2.从Sc中获得ac容器
	WebApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(sc);
	//3.从容器中获得CustomerService
	CustomerService cs = (CustomerService) ac.getBean("customerService");
2、指定配置文件的位置

spring配置文件名字任意,位置任意,创建spring容器时需指定名字和位置,用以下方式指定配置文件的名字和位置:

  <!-- 指定加载spring配置文件的位置 -->
  <context-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext.xml</param-value>
  </context-param>

spring中使用注解代替xml配置

准备工作

1.导包
在这里插入图片描述
2.为主配置文件导入context命名空间(约束)
在这里插入图片描述
在这里插入图片描述
成果:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://www.springframework.org/schema/beans" 
xmlns:context="http://www.springframework.org/schema/context" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.2.xsd ">

3.开启使用注解代替配置文件

<!-- 指定扫描com.xdh.bean包下的所有类中的注解(包含子包) -->
<context:component-scan base-package="com.xdh.bean"></context:component-scan>

component-scan:组件扫描,在这里,component理解为对象

具体使用

1.在类中使用注解

@Component("user")
public class User {
}

@Component注解:将对象注册到容器,参数是对象在容器中的名称
以下三个注解和@Component作用一样,只是为了区分包方便,一般用以下三个注解
@Service(“user”) // service层
@Controller(“user”) // web层
@Repository(“user”)// dao层
相当于:<bean name="user" class="com.xdh.bean.User"></bean>

2.修改对象的作用范围

@Component("user")
//指定对象的作用范围
@Scope(scopeName="singleton")
//singleton单例(默认值)
//prototype多例
public class User {
}

3.属性注入
(1)值类型注入

  • 加在成员变量上
	@Value(value="my")
	private String name;

通过反射field赋值,破坏了封装性
注解中只有一个属性需要赋值,并且属性名为value时,键可省略,即@Value(“my”)

  • 或set方法上
	@Value("my")
	public void setName(String name) {
		this.name = name;
	}

通过set方法赋值,推荐使用
(2)引用类型注入
user对象中注入car步骤:

  • 先将car注入到spring容器中
@Component("car2")
public class Car {
  • 把car对象注入到user中:
    方式一:自动装配
	@Autowired
	@Qualifier("car2")
	private Car car;

问题:如果匹配多个类型一致的对象.将无法选择具体注入哪一个对象.
@Qualifier(“car2”)//使用@Qualifier注解告诉spring容器自动装配哪个名称的对象

方式二:手动装配,推荐使用

@Resource(name="car2")
	private Car car;

手动注入,指定注入哪个名称的对象

4.初始化和销毁方法

	@PostConstruct //在对象被创建后调用.init-method
	public void init(){
		System.out.println("我是初始化方法!");
	}
	@PreDestroy //在销毁之前调用.destory-method
	public void destroy(){
		System.out.println("我是销毁方法!");
	}
	

spring整合Junit

不需要再在测试demo里每个方法都创建容器和获取对象了
1.导包
在这里插入图片描述
2.配置注解

import com.xdh.bean.User;
//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class TestDemo {
	//将名为user的对象注入到u变量中
	@Resource(name="user")
	private User u;
	@Test
	public void fun1(){
		System.out.println(u);
	}
}

spring中的aop

aop思想:横向重复,纵向抽取,即面向切面编程
例如,过滤器,动态代理,拦截器

动态代理:
Proxy.newProxyInstance(classload,interface[],InvocationHandler)
classload:告诉虚拟机用哪个字节码加载器加载内存中创建出的字节码文件
interface:被代理对象实现的接口
InvocationHandler:存放需要被加到被代理对象中的代码

1.spring中的aop概念

spring能够为容器中管理的对象生成动态代理对象
以前要使用动态代理,需要手动调用Proxy.newProxyInstance(classload,interface[],InvocationHandler),生成代理对象

spring能帮我们生成代理对象

2.spring实现aop的原理

(1)动态代理技术
被代理对象必须要实现接口,才能产生代理对象,如果没有接口,将不能使用动态代理技术
(2)cglib代理技术
第三方代理技术,可以对任何类生成代理,代理的原理是对目标对象进行继承代理,如果目标对象被final修饰,那么该类无法被cglib代理

手动实现动态代理的demo:

public interface UserService {
	void save();
	void delete();
	void update();
	void find();
}
public class UserServiceImpl implements UserService {
	@Override
	public void save() {
		System.out.println("保存用户!");
		//int i = 1/0;
	}
	@Override
	public void delete() {
		System.out.println("删除用户!");
	}
	@Override
	public void update() {
		System.out.println("更新用户!");
	}
	@Override
	public void find() {
		System.out.println("查找用户!");
	}
}
package com.xdh.proxydemo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import com.xdh.service.UserService;
import com.xdh.service.UserServiceImpl;

public class UserServiceProxyFactory implements InvocationHandler {
	
	public UserServiceProxyFactory(UserService us) {
		super();
		this.us = us;
	}

	private UserService us;
	
	public UserService getUserServiceProxy(){
		//生成动态代理
		UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
									UserServiceImpl.class.getInterfaces(), 
									this);
		//返回
		return usProxy;
		
	}

	@Override
	//arg0当前代理对象
	//method当前方法
	//arg2当前方法执行的参数
	public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
		System.out.println("打开事务!");
		//执行业务方法
		Object invoke = method.invoke(us, arg2);
		System.out.println("提交事务!");
		return invoke;
	}

}


	@Test
	//动态代理
	public void fun1(){
		UserService us = new UserServiceImpl();
		UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
		UserService usProxy = factory.getUserServiceProxy();
		usProxy.save();
		
		//代理对象与被代理对象实现了相同的接口
		//代理对象 与 被代理对象没有继承关系
		System.out.println(usProxy instanceof UserServiceImpl );//false
	}

手动实现cglib代理的demo:

package com.xdh.proxydemo;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.xdh.service.UserService;
import com.xdh.service.UserServiceImpl;

public class UserServiceProxyFactory2 implements MethodInterceptor {
	

	public UserService getUserServiceProxy(){
		
		Enhancer en = new Enhancer();//帮我们生成代理对象
		
		en.setSuperclass(UserServiceImpl.class);//设置对谁进行代理
		
		en.setCallback(this);//代理要做什么
		
		UserService us = (UserService) en.create();//创建代理对象
		
		return us;
	}

	@Override
	public Object intercept(Object prxoyobj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
		//打开事务
		System.out.println("打开事务!");
		//调用原有方法
		Object returnValue = methodProxy.invokeSuper(prxoyobj, arg);
		//提交事务
		System.out.println("提交事务!");
		
		return returnValue;
	}


}
	@Test
	public void fun2(){
		UserServiceProxyFactory2 factory = new UserServiceProxyFactory2();
		UserService usProxy = factory.getUserServiceProxy();
		usProxy.save();
		
		//判断代理对象是否属于被代理对象类型
		//代理对象继承了被代理对象=>true
		System.out.println(usProxy instanceof UserServiceImpl );//true
	}

总结:
(1)优先使用动态代理,如果没有接口,则使用cglib代理
(2)动态代理里,代理对象与被代理对象实现了相同的接口,代理对象与被代理对象 没有继承关系
cglib代理里,代理对象继承了被代理对象

3.spring的aop名词解释

Jointpoint(连接点):目标对象中,所有可以增强的方法
比如UserServiceImpl中的增删改查方法

Pointcut(切入点):目标对象中已经增强的方法
比如UserServiceImpl中的增删改方法

Advice(通知/增强):增强的代理
比如开启事务和提交/回滚事务的方法

Target(目标对象):被代理对象UserServiceImpl

Weaving(织入):将通知应用到切入点的过程

Proxy(代理):将通知织入到目标对象之后,形成代理对象

aspect(切面):切入点+通知

4.spring的aop准备工作

(1)xml配置
1)导包
在这里插入图片描述
2)准备目标对象

public class UserServiceImpl implements UserService {
	@Override
	public void save() {
		System.out.println("保存用户!");
		//int i = 1/0;
	}
	@Override
	public void delete() {
		System.out.println("删除用户!");
	}
	@Override
	public void update() {
		System.out.println("更新用户!");
	}
	@Override
	public void find() {
		System.out.println("查找用户!");
	}
}

3)准备通知

package com.xdh.aopdemo;
import org.aspectj.lang.ProceedingJoinPoint;

public class MyAdvice {
	//前置通知
	public void before(){
		System.out.println("这是前置通知!!");
	}
	//后置通知
	public void afterReturning(){
		System.out.println("这是后置通知(如果出现异常不会调用)!!");
	}
	//环绕通知
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("这是环绕通知之前的部分!!");
		Object proceed = pjp.proceed();//调用目标方法
		System.out.println("这是环绕通知之后的部分!!");
		return proceed;
	}
	//异常通知
	public void afterException(){
		System.out.println("出事啦!出现异常了!!");
	}
	//后置通知
	public void after(){
		System.out.println("这是后置通知(出现异常也会调用)!!");
	}

}

4)配置进行织入,将通知织入到目标对象中
先导入aop命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://www.springframework.org/schema/beans" 
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-4.2.xsd 
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-4.2.xsd 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">

<!-- 准备工作: 导入aop(约束)命名空间 -->
<!-- 1.配置目标对象 -->
	<bean name="userService" class="com.xdh.service.UserServiceImpl" ></bean>
<!-- 2.配置通知对象 -->
	<bean name="myAdvice" class="com.xdh.aopdemo.MyAdvice" ></bean>
<!-- 3.配置将通知织入目标对象 -->
	<aop:config>
		<!-- 配置切入点 
			id:切入点的名字
		-->
		<aop:pointcut expression="execution(* com.xdh.service.*ServiceImpl.*(..))" id="pc"/>
		<!-- ref属性:通知对象名称 -->
		<aop:aspect ref="myAdvice" >
			<!-- 指定名为before方法作为前置通知 -->
			<aop:before method="before" pointcut-ref="pc" />
			<!-- 后置 -->
			<aop:after-returning method="afterReturning" pointcut-ref="pc" />
			<!-- 环绕通知 -->
			<aop:around method="around" pointcut-ref="pc" />
			<!-- 异常拦截通知 -->
			<aop:after-throwing method="afterException" pointcut-ref="pc"/>
			<!-- 后置 -->
			<aop:after method="after" pointcut-ref="pc"/>
		</aop:aspect>
	</aop:config>
	
</beans>

切入点表达式:
public void com.xdh.service.UserServiceImpl.save()
一般public可省略,public是默认值
切入点对返回值没有要求
不仅对save()方法增强
方法的参数不做要求
还可对service的子包下的方法进行增强
即 * com.xdh.service…ServiceImpl.(…)

测试:

//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定创建容器时使用哪个配置文件
@ContextConfiguration("classpath:com/xdh/aopdemo/applicationContext.xml")
public class TestAop {
	//将名为userService的对象注入到us变量中
	//这里userService是代理对象,获取不到被代理对象了
	@Resource(name="userService")
	private UserService us;
	
	@Test
	public void fun1(){
		us.save();
	}
}

(2)注解配置
1)导包
2)准备目标对象
3)准备通知
4)配置进行织入,将通知织入到目标对象中

<!-- 1.配置目标对象 -->
	<bean name="userService" class="com.xdh.service.UserServiceImpl" ></bean>
<!-- 2.配置通知对象 -->
	<bean name="myAdvice" class="com.xdh.annotationaop.MyAdvice" ></bean>
<!-- 3.开启使用注解完成织入 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
package com.xdh.annotationaop;
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;

//通知类
@Aspect
//表示该类是一个通知类
public class MyAdvice {
	@Pointcut("execution(* com.xdh.service.*ServiceImpl.*(..))")
	public void pc(){}
	//前置通知
	//指定该方法是前置通知,并制定切入点
	//找MyAdvice下的pc()方法,自动找方法上的注解
	@Before("MyAdvice.pc()")
	public void before(){
		System.out.println("这是前置通知!!");
	}
	//后置通知
	@AfterReturning("execution(* com.xdh.service.*ServiceImpl.*(..))")
	public void afterReturning(){
		System.out.println("这是后置通知(如果出现异常不会调用)!!");
	}
	//环绕通知
	@Around("execution(* com.xdh.service.*ServiceImpl.*(..))")
	public Object around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("这是环绕通知之前的部分!!");
		Object proceed = pjp.proceed();//调用目标方法
		System.out.println("这是环绕通知之后的部分!!");
		return proceed;
	}
	//异常通知
	@AfterThrowing("execution(* com.xdh.service.*ServiceImpl.*(..))")
	public void afterException(){
		System.out.println("出事啦!出现异常了!!");
	}
	//后置通知
	@After("execution(* com.xdh.service.*ServiceImpl.*(..))")
	public void after(){
		System.out.println("这是后置通知(出现异常也会调用)!!");
	}

}

spring-jdbc

spring中提供了一个可以操作数据库的对象,即JDBCTemplate,该对象封装了JDBC技术
JDBCTemplate:JDBC模板对象
与DBUtils中的QueryRunner非常相似

1.步骤

(1) 导包

  • 4个spring核心包
  • 2个日志包
  • test包
  • c3p0连接池
  • JDBC驱动包
  • spring-jdbc(JDBCTemplate在这个包里)
  • spring-tx事务包
    在这里插入图片描述

JDBCTemplate操作数据库的demo:

//0 准备连接池
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql:///springtest");
dataSource.setUser("root");
dataSource.setPassword("root");
//1 创建JDBC模板对象
JdbcTemplate jt = new JdbcTemplate();
jt.setDataSource(dataSource);
//2 书写sql,并执行
String sql = "insert into user(id,name,psw) values(null,'rose','123456') ";
jt.update(sql);

以上是自己手动new JdbcTemplate,现将dao,JdbcTemplate,datasorce都交由spring容器管理!

(2) 书写Dao

public interface UserDao {
	//增
	void save(User u);
	//删
	void delete(Integer id);
	//改
	void update(User u);
	//查
	User getById(Integer id);
	//查
	int getTotalCount();
	//查
	List<User> getAll();
}
package com.xdh.dao;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import com.xdh.bean.User;

public class UserDaoImpl implements UserDao{
	private JdbcTemplate jt;
	@Override
	public void save(User u) {
		String sql = "insert into user values(null,?,?) ";
		jt.update(sql, u.getName(),u.getPsw());
	}

	@Override
	public void delete(Integer id) {
		String sql = "delete from user where id = ? ";
		jt.update(sql,id);
	}

	@Override
	public void update(User u) {
		String sql = "update  user set name = ?,psw = ? where id=? ";
		jt.update(sql, u.getName(),u.getPsw(),u.getId());
	}

	@Override
	public User getById(Integer id) {
		String sql = "select * from user where id = ? ";
		//查询结果返回单个对象
		//RowMapper<T>:手动封装结果集,底层是遍历ResultSet
		return  jt.queryForObject(sql, new RowMapper<User>(){
			@Override
			//mapRow()方法已经对结果进行非空判断了,非空才会执行该方法
			public User mapRow(ResultSet rs, int arg1) throws SQLException {
				// TODO Auto-generated method stub
				User u = new User();
				u.setId(rs.getInt("id"));
				u.setName(rs.getString("name"));
				return u;
			}}, id);
	}

	@Override
	public int getTotalCount() {
		String sql = "select count(*) from user";
		//当查询结果返回值类型,Class<T> requiredType参数直接是想封装成哪种类型的值类型
		Integer count = jt.queryForObject(sql, Integer.class);
		return count;
	}

	@Override
	public List<User> getAll() {
		String sql = "select * from t_user  ";
		List<User> list = jt.query(sql, new RowMapper<User>(){
			@Override
			public User mapRow(ResultSet rs, int arg1) throws SQLException {
				User u = new User();
				u.setId(rs.getInt("id"));
				u.setName(rs.getString("name"));
				u.setPsw(rs.getString("psw"));
				return u;
			}});
		return list;
	}

	public JdbcTemplate getJt() {
		return jt;
	}

	public void setJt(JdbcTemplate jt) {
		this.jt = jt;
	}

}

(3) 将对象配置到spring容器中让容器进行管理
dao依赖JdbcTemplate,JdbcTemplate依赖datasource

<!-- 1.将连接池放入spring容器 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
	<property name="jdbcUrl" value="jdbc:mysql:///springtest" ></property>
	<property name="driverClass" value="com.mysql.jdbc.Driver" ></property>
	<property name="user" value="root" ></property>
	<property name="password" value="root" ></property>
</bean>

<!-- 2.将JDBCTemplate放入spring容器 -->
<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
	<property name="dataSource" ref="dataSource" ></property>
</bean>

<!-- 3.将UserDao放入spring容器 -->
<bean name="userDao" class="com.xdh.dao.UserDaoImpl" >
	<property name="jt" ref="jdbcTemplate" ></property>
</bean>

扩展1:

在UserDaoImpl中手动生成private JdbcTemplate jt;对象,
可让UserDaoImpl extends JdbcDaoSupport代替手动生成JdbcTemplate 对象
在JdbcDaoSupport里,会根据连接池帮忙创建JdbcTemplate 对象

因此上述代码可改成:

public class UserDaoImpl extends JdbcDaoSupport implements UserDao{
	@Override
	public void save(User u) {
		String sql = "insert into user values(null,?,?) ";
		super.getJdbcTemplate().update(sql, u.getName(),u.getPsw());
	}

<!-- 1.将连接池放入spring容器 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
	<property name="jdbcUrl" value="jdbc:mysql:///springtest" ></property>
	<property name="driverClass" value="com.mysql.jdbc.Driver" ></property>
	<property name="user" value="root" ></property>
	<property name="password" value="root" ></property>
</bean>

<!-- 2.将UserDao放入spring容器 -->
<bean name="userDao" class="com.xdh.dao.UserDaoImpl" >
	<property name="dataSource" ref="dataSource" ></property>
</bean>

扩展2:
读取外部文件配置datasoure

<!-- 指定spring读取db.properties配置 -->
<context:property-placeholder location="classpath:db.properties"  />

<!-- 1.将连接池放入spring容器 -->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
	<property name="jdbcUrl" value="${jdbc.jdbcUrl}" ></property>
	<property name="driverClass" value="${jdbc.driverClass}" ></property>
	<property name="user" value="${jdbc.user}" ></property>
	<property name="password" value="${jdbc.password}" ></property>
</bean>

2.spring中的aop事务

(1)事务操作对象

因为在不同平台,操作事务的代码各不相同,因此spring提供了一个接口:PlatformTransactionManager

各平台自己提供实现类,例如:
JDBC:DataSourceTransactionManager
Hibernate:HibernateTransactionManager

spring中的事务管理,最核心的对象就是TransactionManager

(2)spring管理事务的属性

1)事务的隔离级别

  • 读未提交:1
  • 读已提交:2
  • 可重复读:4
  • 串行化:8

2)是否只读

  • 只读:true
  • 可操作:false

3)事务的传播行为
即页面方法之间调用,事务应该如何处理

  • PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。(默认,且最常用)
  • PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
  • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
(3)spring管理事务方式

1)编码式(在方法里写事务,不常用)
步骤:

  • 将核心事务管理器DataSourceTransactionManager配置到spring容器
<!-- 事务核心管理器,封装了所有事务操作. 依赖于连接池 -->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
	<property name="dataSource" ref="dataSource" ></property>
</bean>
  • 配置事务模板对象TransactionTemplate
<!-- 事务模板对象 -->
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate" >
	<property name="transactionManager" ref="transactionManager" ></property>
</bean>
  • 将事务模板注入到service
<bean name="accountService" class="com.xdh.service.AccountServiceImpl" >
	<property name="ad" ref="accountDao" ></property>
	<property name="tt" ref="transactionTemplate" ></property>
</bean> 
  • 在service中调用模板
package com.xdh.service;

import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import com.xdh.dao.AccountDao;

public class AccountServiceImpl implements AccountService {

	private AccountDao ad ;
	private TransactionTemplate tt;
	
	@Override
	public void transfer(final Integer from,final Integer to,final Double money) {
		
		tt.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus arg0) {
				//减钱
				ad.decreaseMoney(from, money);
				int i = 1/0;
				//加钱
				ad.increaseMoney(to, money);
			}
		});
	}
	
	public void setAd(AccountDao ad) {
		this.ad = ad;
	}

	public void setTt(TransactionTemplate tt) {
		this.tt = tt;
	}
	
}

2)xml配置(aop)
aop工作流程:准备目标对象,准备通知,将通知织入到目标对象中
这里目标对象AccountServiceImpl,事务通知由spring准备,配置进行织入
步骤:

  • 导包(即aop需要的包:aop、aspect、aop联盟、weaving织入包)
  • 导入tx命名空间
  • 配置通知
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
	<tx:attributes>
		<!-- 以方法为单位,指定方法应用什么事务属性
			isolation:隔离级别
			propagation:传播行为
			read-only:是否只读
		 -->
		<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
	</tx:attributes>
</tx:advice>

实际开发中用通配符:

<tx:advice id="txAdvice" transaction-manager="transactionManager" >
	<tx:attributes>
		<tx:method name="save*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="persist*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="modify*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="delete*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="remove*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
		<tx:method name="get*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" />
		<tx:method name="find*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" />
	</tx:attributes>
</tx:advice>
  • 配置将通知织入目标对象
<!-- 配置织入 -->
<aop:config  >
	<!-- 配置切点表达式 -->
	<aop:pointcut expression="execution(* com.xdh.service.*ServiceImpl.*(..))" id="txPc"/>
	<!-- 配置切面 : 通知+切点
		 	advice-ref:通知的名称
		 	pointcut-ref:切点的名称
	 -->
	 <!-- aop:aspect手动配置通知方法 -->
	<aop:advisor advice-ref="txAdvice" pointcut-ref="txPc" />
</aop:config>

3)注解配置(aop)
步骤:导包、约束和xml配置一样,其他步骤:

  • 开启注解管理事务
<!-- 开启使用注解管理aop事务 -->
<tx:annotation-driven/>
  • 使用注解

1)加在方法上

	@Override
	@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false)
	public void transfer(final Integer from,final Integer to,final Double money) {
		//减钱
		ad.decreaseMoney(from, money);
		int i = 1/0;
		//加钱
		ad.increaseMoney(to, money);
}

2)加在类上

@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false)
public class AccountServiceImpl implements AccountService {
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值