一、初识Spring
1.Spring是分层的JavaSE/EE full-stack(一站式) 轻量级开源框架
EE的三层结构:web层、业务层、数据访问层(持久层,集成层)
2.Spring的核心:IOC(控制反转)、AOP(面向切面编程)
3.学习Spring需要的入门准备(①下载Spring开发包 ②创建web工程引入相应的jar包 ③创建Spring配置文件④在配置中配置类⑤创建测试类)
Spring的入门级案例:
1.创建一个接口
package org.llj.springdemo;
public interface HelloService {
public void sayHello();
}
2.创建该接口的实现类
public class HelloServiceImpl implements HelloService {
private String info;
public void setInfo(String info) {
this.info = info;
}
@Override
public void sayHello() {
System.out.println("hello spring"+info);
}
}
3.配置配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 通过bean 标签设置类的信息,为类起一个标识 -->
<bean id="userService" class="org.llj.springdemo.HelloServiceImpl">
<!-- 通过property标签注入属性 -->
<property name="info" value="哈哈哈哈哈"></property>
</bean>
</beans>
4.编写测试类:
public class ServiceTest {
@Test
//传统方式的测试
public void demo1(){
HelloService helloService=new HelloServiceImpl();
helloService.sayHello();
}
@Test
//采用spring框架测试
public void demo2(){
org.springframework.context.ApplicationContext applicationContext
=new ClassPathXmlApplicationContext("applicationContext.xml");
HelloService helloService=(HelloService) applicationContext.getBean("userService");
helloService.sayHello();
}
@Test
public void demo3(){
org.springframework.context.ApplicationContext applicationContext
=new FileSystemXmlApplicationContext("applicationContext.xml");
HelloService helloService=(HelloService) applicationContext.getBean("userService");
helloService.sayHello();
}
}
4.DI:依赖注入:在Spring创建对象的过程中,把对象依赖的属性注入到类中.
5.IOC装配bean的方式(三种)
①构造方法实例化:
创建要实例化的类
package org.llj.springdemo2;
/**
* 使用无参数的构造方法实例化
* @author lenovo
*
*/
public class bean1 {
}
加载配置文件
<!--默认情况使用的是无参数的构造方法-->
<!-- <bean id="bean1" class="org.llj.springdemo2.bean1"></bean> -->
编写测试类
@Test
public void demo1(){
//1.加载类
ApplicationContext applicationContext=new ClassPathXmlApplicationContext
("ApplicationContext.xml");
bean1 bean1=(org.llj.springdemo2.bean1) applicationContext.getBean("bean1");
System.out.println(bean1);
}
②静态工厂实例化
package org.llj.springdemo2;
/**
* 使用静态工厂实例化
* @author lenovo
*
*/
public class bean2 {
}
package org.llj.springdemo2;
public class bean2Factory {
public static bean2 getBean2(){
System.out.println("静态工厂实例化");
return new bean2();
}
}
加载配置文件:
<!--第二种,使用静态工厂实例化 -->
<bean id="bean2" class="org.llj.springdemo2.bean2Factory"
factory-method="getBean2"></bean>
测试:
@Test
public void demo2(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext
("ApplicationContext.xml");
bean2 bean2=(org.llj.springdemo2.bean2) applicationContext.getBean("bean2");
System.out.println(bean2);
}
③实例工厂实例化
package org.llj.springdemo2;
/**
* 使用实例工厂的方法实例化
* @author lenovo
*
*/
public class bean3 {
}
package org.llj.springdemo2;
/**
* 使用实例工厂实例化
* @author lenovo
*
*/
public class bean3Factory {
public bean3 getBean3(){
System.out.println("实例工厂实例化");
return new bean3();
}
}
配置文件:
<!-- 第三种,使用实例工厂实例化 -->
<bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3"></bean>
<bean id="bean3Factory" class="org.llj.springdemo2.bean3Factory" ></bean>
测试:
@Test
public void demo3(){
ApplicationContext applicationContext=new ClassPathXmlApplicationContext
("ApplicationContext.xml");
bean3 bean3=(org.llj.springdemo2.bean3) applicationContext.getBean("bean3");
System.out.println(bean3);
}
6.bean中属性注入:
①构造器的方法注入:
<bean id="car" class="org.llj.springdemo5.Car">
<constructor-arg name="name" value="宝马"></constructor-arg>
<constructor-arg name="price" value="1000000"></constructor-arg>
<!-- 可以用位置索引的方式 -->
<constructor-arg index="0" value="奔驰"></constructor-arg>
<constructor-arg index="1" value="500000"></constructor-arg>
</bean>
②setter方法注入:
<bean id="car2" class="org.llj.springdemo5.Car2">
property 标签中name就是属性名称,value就是属性值 ref表示引用的其他对象
<property name="name" value="兰博基尼"></property>
<property name="price" value="50000000"></property>
</bean>
<bean id="car2" class="org.llj.springdemo5.Car2" p:name="宝马" p:price="400000"></bean>
③名称空间p注入:
<!-- p的名称空间写法 -->
<bean id="person" class="org.llj.springdemo5.Person" p:name="娟儿" p:car-ref="car2">
</bean>
④spel写法
<bean id="person" class="org.llj.springdemo5.Person">
value的#号后的大括号如果有单引号,说明是常量;如果没有单引号,说明是引用类型
<property name="name" value="#{'小超'}"></property>
<property name="car" value="#{car2}"></property>
</bean>
⑤集合注入
<!-- demo6 List集合的属性注入 -->
<bean id="collectionBean" class="org.llj.springdemo6.CollectionBean">
<property name="list">
<list>
<value>梅</value>
<value>玲</value>
</list>
</property>
<!-- set集合注入 -->
<property name="set">
<set>
<value>花花</value>
<value>红军</value>
</set>
</property>
<!-- map集合注入 -->
<property name="map">
<map>
<entry key="小牛" value="456"></entry>
<entry key="小猫" value="789"></entry>
</map>
</property>
⑥propeties注入:
<property name="properties">
<props>
<prop key="username" >root</prop>
<prop key="password">123</prop>
</props>
</property>
</bean>
7.IOC注解方式装配bean
Spring的框架中提供了与@Component注解等效的三个注解:
@Repository 用于对DAO实现类进行标注
@Service 用于对Service实现类进行标注
@Controller 用于对Controller实现类进行标注
普通属性;
@Value(value="itcast")
private String info;
对象属性:
@Autowired:自动装配默认使用类型注入.
@Autowired
@Qualifier("userDao") --- 按名称进行注入.
@Autowired
@Qualifier("userDao")
private UserDao userDao;
等价于
@Resource(name="userDao")
private UserDao userDao;
8.spring整合JUNIT测试
1.程序中有Junit环境.
2.导入一个jar包.spring与junit整合jar包.
* spring-test-3.2.0.RELEASE.jar
3.测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext2.xml")
public class springTest {
@Autowired
private UserService userservice;
@Test
public void demo1(){
userservice.sayhello();
}
}
二、spring中的AOP
AOP的底层实现:
1.JDK动态代理
public class JDKProxy implements InvocationHandler{
private UserDao userDao;
public JDKProxy(UserDao userDao) {
super();
this.userDao = userDao;
}
public UserDao createProxy(){
//第一个参数为类装载器,第二个参数是真实类所拥有的接口,第三个数组则是当前类
UserDao proxy=(UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(), this);
return proxy;
}
//调用目标对象的任意一个方法,都相当于调用了Invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
if("add".equals(method.getName())){
//记录日志
System.out.println("日志记录======================");
//执行add方法
Object result=method.invoke(userDao, args);
return result;
}else {
return method.invoke(userDao, args);
}
}
}
2.CGLIB动态代理
public class CGLibProxy implements MethodInterceptor {
private ProductDao productDao;
public CGLibProxy(ProductDao productDao) {
super();
this.productDao = productDao;
}
public ProductDao createProxy(){
//使用CGLib生成代理
//1.创建核心类:Enhancer
Enhancer enhancer=new Enhancer();
//2.为其设置父类
enhancer.setSuperclass(productDao.getClass());
//3.设置回调
enhancer.setCallback(this);
//4.创建代理
return (ProductDao) enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if("add".equals(method.getName())){
System.out.println("日志记录=================");
Object object=methodProxy.invokeSuper(proxy, args);
return object;
}
return methodProxy.invokeSuper(proxy, args);
}
}
Spring框架,如果类实现了接口,就使用JDK的动态代理生成代理对象,如果这个类没有实现任何接口,使用CGLIB生成代理对象.
Spring中AspectJ的支持
AspectJ增强:
@Before 前置通知,相当于BeforeAdvice
@AfterReturning 后置通知,相当于AfterReturningAdvice
@Around 环绕通知,相当于MethodInterceptor
@AfterThrowing抛出通知,相当于ThrowAdvice
@After 最终final通知,不管是否异常,该通知都会执行
@DeclareParents 引介通知,相当于IntroductionInterceptor (不要求掌握)
1.基于注解的
<!--自动生成代理 -->
<aop:aspectj-autoproxy />
<bean id="userDao" class="org.llj.spring.demo1.UserDao"></bean>
<bean id="myAspect" class="org.llj.spring.demo1.MyAspect"></bean>
import org.aspectj.lang.JoinPoint;
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;
/**
* 切面类,就是切点与增强的结合
* @author lenovo
*
*/
@Aspect
public class MyAspect {
@Before("execution(* org.llj.spring.demo1.UserDao.add(..))")
public void before(JoinPoint joinPoint){
System.out.println("这是一个前置增强……"+joinPoint);
}
@AfterReturning(value="execution(* org.llj.spring.demo1.UserDao.delete(..))",returning="returnVal")
public void after(Object returnVal){
System.out.println("这是一个后置增强……"+returnVal);
}
@Around("MyAspect.mypointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint)throws Throwable{
System.out.println("环绕前增强");
Object obj=proceedingJoinPoint.proceed();
System.out.println("环绕后增强");
return obj;
}
@AfterThrowing(value="MyAspect.mypointcut()",throwing="e")
public void throwing(Throwable e){
System.out.println("不好了。出异常了"+e.getMessage());
}
@After("MyAspect.mypointcut()")
public void after(){
System.out.println("最终通知……");
}
@Pointcut("execution(* org.llj.spring.demo1.UserDao.find(..))")
private void mypointcut(){
}
}
二、基于XML的
package org.llj.spring.demo2;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspectXML {
public void before(){
System.out.println("前置通知……");
}
public void aferReturning(Object returnVal){
System.out.println("后置通知……"+returnVal);
}
//环绕通知
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("环绕前通知……");
Object result=proceedingJoinPoint.proceed();
System.out.println("环绕后通知……");
return result;
}
//异常通知
public void afterThrowing(Throwable e){
System.out.println("出异常了……"+e.getMessage());
}
//最终通知
public void after(){
System.out.println("这是最终通知……");
}
}
<!-- 定义被增强的类 -->
<bean id="productDao" class="org.llj.spring.demo2.ProductDao"></bean>
<!--定义切面 -->
<bean id="myAspectxml" class="org.llj.spring.demo2.MyAspectXML"></bean>
<!--定义aop配置 -->
<aop:config>
<!--定义切点 -->
<aop:pointcut expression="execution(* org.llj.spring.demo2.ProductDao.add(..))" id="mypointcut"/>
<aop:aspect ref="myAspectxml">
<!--前置通知 -->
<!-- <aop:before method="before" pointcut-ref="mypointcut"/> -->
<!-- 后置通知 -->
<!-- <aop:after-returning method="aferReturning" pointcut-ref="mypointcut"
returning="returnVal"
/> -->
<!-- 环绕通知 -->
<!-- <aop:around method="around" pointcut-ref="mypointcut"/> -->
<!-- 异常通知 -->
<!-- <aop:after-throwing method="afterThrowing" pointcut-ref="mypointcut" throwing="e"/> -->
<!--最终通知 -->
<aop:after method="after" pointcut-ref="mypointcut"/>
</aop:aspect>
</aop:config>
Spring的jdbcTemplate
1.jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_day02
jdbc.user=root
jdbc.password=123
2.spring 提供了三种连接池(以下)
①.配置spring默认的连接池
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring_day02"></property>
<property name="username" value="root"></property>
<property name="password" value="123"></property>
</bean>
②.配置dbcp连接池
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql:///spring_day02"></property>
<property name="username" value="root"></property>
<property name="password" value="123"></property>
</bean>
③.-配置c3p0连接池
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
3.定义jdbcTemplate摸板类
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="userDao" class="org.llj.spring.demo2.UserDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<context:property-placeholder location="classpath:jdbc.properties"/>
JdbcTemplate的CRUD的操作
import java.awt.List;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
* jdbcDaoSupport这个类中已经定义了模板,并且有一个set方法,所以这边继承JdbcDaoSupport之后就
* 不需要在定义模板
* @author lenovo
*
*/
public class UserDao extends JdbcDaoSupport {
public void add(User user){
String sql="insert into user values(null,? )";
this.getJdbcTemplate().update(sql,user.getName());
}
public void update(User user){
String sql="update user set name = ? where id = ?";
this.getJdbcTemplate().update(sql, user.getName(),user.getId());
}
public void delete(User user){
String sql="delete from user where id = ?";
this.getJdbcTemplate().update(sql, user.getId());
}
public String findNameById(int id){
String sql="select name from user where id = ?";
return this.getJdbcTemplate().queryForObject(sql, String.class, id);
}
public User findById(int id){
String sql="select * from user where id = ?";
User user=(User)this.getJdbcTemplate().queryForObject(sql, new UserRowMapper(),id);
return user;
}
public java.util.List<User> findAll(){
String sql="select * from user";
return this.getJdbcTemplate().query(sql, new UserRowMapper());
}
class UserRowMapper implements RowMapper<User>{
/**
* rs:结果集
* rowNum:行号
*/
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user=new User();
user.setId(rs.getInt("id"));
user.setName(rs.getString("name"));
return user;
}
}
}
测试方法:
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class springTest {
@Autowired
@Qualifier("userDao")
private UserDao userDao;
@Test
public void demo1(){
User user=new User();
user.setName("娟儿");
userDao.add(user);
}
@Test
public void demo2(){
User user=new User();
user.setId(1);
user.setName("珑珠");
userDao.update(user);
}
@Test
public void demo3(){
User user=new User();
user.setId(1);
userDao.delete(user);
}
@Test
public void demo4(){
String name=userDao.findNameById(2);
System.out.println(name);
}
@Test
public void demo5(){
User user=userDao.findById(2);
System.out.println(user);
}
@Test
public void demo6(){
List<User> list=userDao.findAll();
for (User user : list) {
System.out.println(user);
}
}
}
三、Spring之事务管理
Spring的事务管理主要分成两类:
1.编程式事务管理(手动编写代码完成事务的管理)
2.声明式事务管理 (不需要手动编写,配置 )
事务操作的环境搭建:
1.需要有相应的数据库。
2.创建一个动态web项目:导入相应的jar;引入配置文件
3.创建类:XXXService;XXXDao
4.在Spring中注册业务层类和持久层类:
<!--业务层类 -->
<bean id="accountService" class="org.llj.spring.demo1.AccountServiceImpl">
<!--在业务层注入Dao -->
<property name="accountDao" ref="accountDao"></property>
<!--在业务层注入事务管理的模板 -->
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<!-- 持久层类 -->
<bean id="accountDao" class="org.llj.spring.demo1.AccountDaoImpl">
<!--注入连接池对象,通过连接池对象创建模板 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
5.OK,编写测试类,测试环境是否搭建成功。
Spring的事务管理
不管哪种方式都需要事务管理器(TransactionManager)
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--需要注入连接池,通过连接池获得连接 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
一.手动编写的方式完成事务管理
1.注册事务模板:(TransactionTemplate)
<!-- 事务管理的模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
2.在业务层注入模板类:(模板管理事务)
<!--业务层类 -->
<bean id="accountService" class="org.llj.spring.demo1.AccountServiceImpl">
<!--在业务层注入Dao -->
<property name="accountDao" ref="accountDao"></property>
<!--在业务层注入事务管理的模板 -->
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
3.在业务层代码上使用模板:(以下是我的业务层代码)
package org.llj.spring.demo1;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
private TransactionTemplate transactionTemplate;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void transfer(final String from, final String to, final Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
// TODO Auto-generated method stub
accountDao.out(from, money);
int i=10/0;
accountDao.in(to, money);
}
});
}
}
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
// TODO Auto-generated method stub
accountDao.out(from, money);
int i=10/0;
accountDao.in(to, money);
}
});
}
}
二.原始的声明式事务管理:
1.创建业务层代理对象(飘蓝的为属性设置,如果不写,则系统默认)
<!-- 配置生成代理对象 -->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 目标对象 -->
<property name="target" ref="accountService"/>
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"/>
<!-- 事务的属性设置 -->
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<prop key="transfer">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
2.编写测试类(注意注入的是代理对象)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class springTest2 {
@Autowired
@Qualifier("accountServiceProxy")
private AccountService accountService;
@Test
public void demo1(){
accountService.transfer("aaa", "bbb", 100d);
}
}
"accountServiceProxy")
private AccountService accountService;
@Test
public void demo1(){
accountService.transfer("aaa", "bbb", 100d);
}
}
三.声明式事务管理(基于切面)
1.导入所需的jar包,引入配置文件(aop、tx的约束)
2.事务管理(定义增强)
<!--定义一个增强 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--增强事务的属性配置 -->
<tx:attributes>
<!--
isolation="DEFAULT": 事务的隔离级别
propagation="REQUIRED" 事务的传播行为
read-only="false" 事务是否只读,默认为否
no-rollback-for="" 发生哪些异常不回滚
no-rollback-for="" 发生哪些异常回滚
这些不设置的话,他会有一个默认值
-->
<tx:method name="transfer" />
</tx:attributes>
</tx:advice>
3.定义切点和通知
<!--aop 配置定义切面和切点的信息 -->
<aop:config>
<!--定义切点:哪些类的哪些方法用于增强 -->
<aop:pointcut expression="execution(* org.llj.spring.demo3.AccountService+.*(..))" id="mypointcut"/>
<!--定义切面: -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypointcut"/>
</aop:config>
4.测试(不需要注入代理对象,注入service对象即可)
四.基于注解的事务管理
1.注解事务:
<!--开启标签的事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
2.在service 上使用注解
@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false)
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String from, String to, Double money) {
accountDao.out(from, money);
//int i=10/0;
accountDao.in(to, money);
}
}
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(String from, String to, Double money) {
accountDao.out(from, money);
//int i=10/0;
accountDao.in(to, money);
}
}
以上为spring中的几种事务管理;其中手动编写的方式代码量大,代码具有入侵性
原始的声明式事务管理需要为每一个管理事务的类生成代理,需要为每个类都进行配置。
基于aspect的声明式事务管理是重点。