本帖最后由 逆风TO 于 2019-7-19 09:09 编辑
好记忆不如烂笔头, 能记下点什么, 就记下点什么, 方便后期的巩固 Spring介绍Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。 所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。 优点: IOC:方便解耦合 AOP:对程序进行扩展 轻量级框架 方便与其他框架整合
Spring使用Spring开发包解压后的目录介绍: docs: Spring 开发规范和API libs: Spring jar 包和源代码 schema: Spring 配置文件的约束
DataAccess 用于数据访问,WEB 用于页面显示,核心容器也就是IOC部分。
控制反转(IOC)控制反转(Inversion of Control)是指将对象的创建权反转(交给)Spring。 使用IOC就需要导入IOC相关的包,也就是上图中核心容器中的几个包:beans,context,core,expression四个包。
实现原理传统方式创建对象: UserDAO userDAO=new UserDAO(); 进一步面向接口编程,可以多态: UserDAO userDAO=new UserDAOImpl(); 这种方式的缺点是接口和实现类高耦合,切换底层实现类时,需要修改源代码。程序设计应该满足OCP元祖,在尽量不修改程序源代码的基础上对程序进行扩展。此时,可以使用工厂模式: class BeanFactory{ public static UserDAO getUserDAO(){ return new UserDAOImpl(); } } 此种方式虽然在接口和实现类之间没有耦合,但是接口和工厂之间存在耦合。 使用工厂+反射+配置文件的方式,实现解耦,这也是 Spring 框架 IOC 的底层实现。 //xml配置文件 //<bean id="userDAO" class="xxx.UserDAOImpl"></bean> class BeanFactory{ public static Object getBean(String id){ //解析XML //反射 Class clazz=Class.forName(); return clazz.newInstance(); } } IOC XML 开发在 docs 文件中包含了 xsd-configuration.hmtl 文件。其中定义了 beans schema。 <beans xmlns="http://www.springframework.org/schema/beans" 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="x.y.UserServiceImpl"> </bean> </beans> 调用类: ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService=(UserService)applicationContext.getBean("userService"); userService.save(); IOC 和 DI DI 指依赖注入,其前提是必须有 IOC 的环境,Spring 管理这个类的时候将类的依赖的属性注入进来。 例如,在UserServiceImpl.java中: public class UserServiceImpl implements UserService{ private String name; public void setName(String name){ this.name=name; } public void save(){ System.out.println("save "+name); } } 在配置文件中: <beans xmlns="http://www.springframework.org/schema/beans" 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 id="userService" class="spring.demo1.UserServiceImpl"> <!--配置依赖的属性--> <property name="name" value="tony"/> </bean> </beans> 测试代码: @Test public void demo2(){ //创建Spring工厂 ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService=(UserService)applicationContext.getBean("userService"); userService.save(); } 运行结果: save tony 可以看到,在配置文件中配置的属性,在 Spring 管理该类的时候将其依赖的属性成功进行了设置。如果不使用依赖注入,则无法使用接口,只能使用实现类来进行设置,因为接口中没有该属性。 Spring 的工厂类 bean标签配置 生命周期: 作用范围: 属性注入设置 <bean id="car" class="demo.Car"> <constructor-arg name="name" value="bmw"> <constructor-arg name="price" value="123"> </bean> <bean id="employee" class="demo.Employee"> <property name="name" value="xiaoming"> <property name="car" ref="car"> </bean> <beans xmlns="http://www.springframework.org/schema/beans" //引入p名称空间 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"> </beans> 如果是普通属性: <bean id="car" class="demo.Car" p:name="bmv" p:price="123"> </bean> 如果是引用类型: <bean id="employee" class="demo.Employee" p:name="xiaoming" p:car-ref:"car"> </bean> <bean id="car" class="demo.Car"> <property name="name" value="#{'xiaoming'}"> <property name="car" ref="#{car}"> </bean> <bean id="car" class="demo.Car"> <property name="namelist"> <list> <value>qirui</value> <value>baoma</value> <value>benchi</value> </list> </property> </bean> 多模块开发配置 在加载配置文件的时候,加载多个配置文件 在一个配置文件中引入多个配置文件,通过实现
IOC 注解开发示例 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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 definitions here --> </beans> <context:component-scan base-package="demo1"> 属性如果有set方法,将属性注入的注解添加到set方法 属性没有set方法,将注解添加到属性上。 @Component("UserDao")//相当于配置了一个<bean> 其id为UserDao,对应的类为该类 public class UserDAOImpl implements UserDAO { @Override public void save() { // TODO Auto-generated method stub System.out.println("save"); } } 注解详解 组件注解,用于修饰一个类,将这个类交给 Spring 管理。 有三个衍生的注解,功能类似,也用来修饰类。 @Controller:修饰 web 层类 @Service:修饰 service 层类 @Repository:修饰 dao 层类
2.属性注入 3.其他注解 @PostConstruct 相当于 init-method,用于初始化函数的注解 @PreDestroy 相当于 destroy-method,用于销毁函数的注解 @Scope 作用范围的注解,常用的是默认单例,还有多例 @Scope("prototype")
IOC 的 XML 和注解开发比较 AOP开发AOP 是 Aspect Oriented Programming 的缩写,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,是OOP的延续。 AOP 能够对程序进行增强,在不修改源码的情况下,可以进行权限校验,日志记录,性能监控,事务控制等。 也就是说功能分为两大类,一类是核心业务功能,一类是辅助增强功能。两类功能彼此独立进行开发。比如登录功能是核心业务功能,日志功能是辅助增强功能,如果有需要,将日志和登录编制在一起。辅助功能就称为切面,这种能选择性的、低耦合的把切面和核心业务功能结合的编程思想称为切面编程。 底层实现 JDK 动态代理只能对实现了接口的类产生代理。Cglib 动态代理可以对没有实现接口的类产生代理对象,生成的是子类对象。 使用 JDK 动态代理: public interface UserDao { public void insert(); public void delete(); public void update(); public void query(); } 实现类: public class UserDaoImpl implements UserDao { @Override public void insert() { System.out.println("insert"); } @Override public void delete() { System.out.println("delete"); } @Override public void update() { System.out.println("update"); } @Override public void query() { System.out.println("query"); } } JDK 代理: public class JDKProxy implements InvocationHandler{ private UserDao userDao; public JDKProxy(UserDao userDao){ this.userDao=userDao; } public UserDao createProxy(){ UserDao userDaoProxy=(UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this); return userDaoProxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("update".equals(method.getName())){ System.out.println("权限校验"); return method.invoke(userDao, args); } return method.invoke(userDao, args); } } 通过动态代理增强了 update 函数。 测试类: public class Demo1 { @Test public void demo1(){ UserDao userDao=new UserDaoImpl(); UserDao proxy=new JDKProxy(userDao).createProxy(); proxy.insert(); proxy.delete(); proxy.update(); proxy.query(); } } 运行结果为: insert delete 权限校验 update query CglibCglib 是第三方开源代码生成类库,可以动态添加类的属性和方法。 与上边JDK代理不同,Cglib的使用方式如下: public class CglibProxy implements MethodInterceptor{ //传入增强的对象 private UserDao customerDao; public CglibProxy(UserDao userDao){ this.userDao=userDao; } public UserDao createProxy(){ Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(userDao.getClass()); enhancer.setCallback(this); UserDao proxy=(UserDao)enhancer.create(); return proxy; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if("save".equals(method.getName())){ System.out.println("enhance function"); return methodProxy.invokeSuper(proxy, args); } return methodProxy.invokeSuper(proxy, args); } } 如果实现了接口的类,底层采用JDK代理。如果不是实现了接口的类,底层采用 Cglib代理。 IOC与传统方式的比较 Spring 的 AOP 开发(AspectJ 的 XML 方式)AspectJ 是一个 AOP 的框架,Spring 引入 AspectJ,基于 AspectJ 进行 AOP 的开发。 相关术语 Joinpoint: 连接点,可以被拦截到的点。也就是可以被增强的方法都是连接点。 Pointcut: 切入点,真正被拦截到的点,也就是真正被增强的方法 Advice: 通知,方法层面的增强。对某个方法进行增强的方法,比如对 save 方法进行权限校验,权限校验的方法称为通知。 Introduction: 引介,类层面的增强。 Target: 目标,被增强的对象(类)。 Weaving: 织入,将 advice 应用到 target 的过程。 Proxy: 代理对象,被增强的对象。 Aspect: 切面,多个通知和多个切入点的组合。
使用方法 <?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 definitions here --> </beans> public class ProductDaoImpl implements ProductDao { @Override public void save() { System.out.println("save"); } @Override public void update() { System.out.println("update"); } @Override public void find() { System.out.println("find"); } @Override public void delete() { System.out.println("delete"); } } <bean id="productDao" class="demo1.ProductDaoImpl"></bean> public class MyAspectXML { public void checkPri(){ System.out.println("check auth"); } } <bean id="myAspect" class="demo1.MyAspectXML"></bean> <aop:config> <aop:pointcut expression="execution(* demo1.ProductDaoImpl.save(..))" id="pointcut1"/> <aop:aspect ref="myAspect"> <aop:before method="chechPri" pointcut-ref="pointcut1"/> </aop:aspect> </aop:config>
通知类型 <aop:before method="chechPri" pointcut-ref="pointcut1"/> public void checkPri(JoinPoint joinPoint){ System.out.println("check auth "+joinPoint); } <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/> public void writeLog(Object result){ System.out.println("writeLog "+result); } <aop:around method="around" pointcut-ref="pointcut3"/> public Object around(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("before"); Object result=joinPoint.proceed(); System.out.println("after"); return result; } <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/> public void afterThrowing(Throwable ex){ System.out.println("exception "+ex.getMessage()); } <aop:after method="finallyFunc" pointcut-ref="pointcut4"/> public void finallyFunc(){ System.out.println("finally"); } Spring 切入点表达式 基于 execution 函数完成 语法:[访问修饰符] 方法返回值 包名.类名.方法名(参数) 其中任意字段可以使用*代替表示任意值 Spring 的 AOP 基于 AspectJ 注解开发开发步骤 <?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: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"> </beans> <bean id="orderDao" class="demo1.OrderDao"></bean> public class OrderDao { public void save(){ System.out.println("save order"); } public void update(){ System.out.println("update order"); } public void delete(){ System.out.println("delete order"); } public void find(){ System.out.println("find order"); } } <aop:aspectj-autoproxy/> @Aspect public class MyAspectAnno { @Before(value="execution(* demo1.OrderDao.save(..))") public void before(){ System.out.println("before"); } } <bean id="myAspect" class="demo1.MyAspectAnno"> 注解通知类型 @Before: 前置通知 @AfterReturning: 后置通知
@AfterReturning(value="execution(* demo1.OrderDao.save(..))",returning="result") public void after(Object result){ System.out.println("after "+result); } @Around(value="execution(* demo1.OrderDao.save(..))") public Object around(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("before"); Object obj=joinPoint.proceed(); System.out.println("after"); return obj; } @AfterThrowing(value="execution(* demo1.OrderDao.save(..))",throwing="e") public void afterThrowing(Throwable e){ System.out.println("exception:"+e.getMessage(); } @After(value="execution(* demo1.OrderDao.save(..))") public void after(){ System.out.println("finally"); } @PointCut(value="execution(* demo1.OrderDao.save(..))") private void pointcut1(){} 此时,在上述通知的注解中,value可以替换为该函数名,例如: @After(value="MyAspect.pointcut1()") public void after(){ System.out.println("finally"); } 这个注解的好处是,只需要维护切入点即可,不用在修改时修改每个注解。 Spring 的 JDBC 模板Spring 对持久层也提供了解决方案,也就是 ORM 模块和 JDBC 的模板。针对 JDBC ,提供了 org.springframework.jdbc.core.JdbcTemplate 作为模板类。 使用 JDBC 模板public void demo1(){ //创建连接池 DriverManagerDataSource dataSource=new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql:///spring4"); dataSource.setUsername("root"); dataSource.setPassword("123456"); //创建JDBC模板 JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource); jdbcTemplate.update("insert into account values (null,?,?)", "xiaoming",1000d); } <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource;"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql:///spring4"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate;"> <property name="dataSource" ref="dataSource"></property> </bean> @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class JdbcDemo2 { @Resource(name="jdbcTemplate") private JdbcTemplate jdbcTemplate; @Test public void demo2(){ jdbcTemplate.update("insert into account values (null,?,?)", "xiaolan",1000d); } } 使用开源数据库连接池 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://192.168.66.128/spring4"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://192.168.66.128/spring4"></property> <property name="user" value="root"></property> <property name="password" value="123456"></property> </bean> 首先建立外部属性文件: jdbc.driverClass=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://192.168.66.128/spring4 jdbc.username=root jdbc.password=123456 然后对属性文件进行配置: <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="user" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </bean> CRUD操作insert, update, delete 语句都借助模板的 update 方法进行操作。 public void demo(){ jdbcTemplate.update("insert into account values (null,?,?)", "xiaoda",1000d); jdbcTemplate.update("update account set name=?,money=? where id=?", "xiaoda",1000d,2); jdbcTemplate.update("delete from account where id=?", 6); } 查询操作: public void demo3(){ String name=jdbcTemplate.queryForObject("select name from account where id=?",String.class,5); long count=jdbcTemplate.queryForObject("select count(*) from account",Long.class); } 将返回的结果封装成为类: public void demo4(){ Account account=jdbcTemplate.queryForObject("select * from account where id=?", new MyRowMapper(),5); } 其中: class MyRowMapper implements RowMapper<Account>{ @Override public Account mapRow(ResultSet rs, int rowNum) throws SQLException { Account account=new Account(); account.setId(rs.getInt("id")); account.setName(rs.getString("name")); account.setMoney(rs.getDouble("money")); return account; } } Spring的事务管理事务事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。 具有四个特性: 原子性:事务不可分 一致性:事务执行前后数据完整性保持一致 隔离性:一个事务的执行不应该受到其他事务干扰 持久性:一旦事务结束,数据就持久化到数据库
如果不考虑隔离性会引发安全性问题: 解决读问题:设置事务隔离级别 Read uncommitted: 未提交读,无法解决任何读问题 Read committed: 已提交读,解决脏读问题 Repeatable read: 重复读,解决脏读和不可重复读问题 Serializable:序列化,解决所有读问题
事务管理API这是一个接口,拥有多个不同的实现类,如 DataSourceTransactionManager 底层使用了JDBC 管理事务; HibernateTransactionManager 底层使用了 Hibernate 管理事务。 用于定义事务的相关信息,如隔离级别、超时信息、传播行为、是否只读等 用于记录在事务管理过程中,事务的状态的对象。 上述API的关系: Spring 在进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务管理,在事务管理过程当中,产生各种此状态,将这些状态信息记录到事务状态的对象当中。 事务的传播行为事务的传播行为主要解决业务层(Service)方法相互调用的问题,也就是不同的业务中存在不同的事务时,如何操作。 Spring 中提供了7种事务的传播行为,分为三类: 保证多个操作在同一个事务中
PROPAGATION_REQUIRED: B方法调用A方法,如果A中有事务,使用A中的事务并将B中的操作包含到该事务中;否则新建一个事务,将A和B中的操作包含进来。(默认) PROPAGATION_SUPPORTS:如果A中有事务,使用A的事务;否则不使用事务 PROPAGATION_MANDATORY:如果A中有事务,使用A的事务;否则抛出异常
保证多个操作不在同一个事务中
PROPAGATION_REQUIRES_NEW:如果A中有事务,将其挂起,创建新事务,只包含自身操作。否则,新建一个事务,只包含自身操作。 PROPAGATION_NOT_SUPPORTED:如果A中有事务,挂起,不使用事务。 PROPAGATION_NEVER:如果A中有事务,抛出异常,也即不能用事务运行。
嵌套事务
实例以转账为例,业务层的DAO层类如下: public interface AccountDao { public void outMoney(String from,Double money); public void inMoney(String to,Double money); } public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{ @Override public void outMoney(String from, Double money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from); } @Override public void inMoney(String to, Double money) { this.getJdbcTemplate().update("update account set money = money + ? where name = ?",money,to); } } public interface AccountService { public void transfer(String from,String to,Double 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.outMoney(from, money); accountDao.inMoney(to, money); } } 在xml中进行类的配置: <bean id="accountService" class="tx.demo.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> </bean> <bean id="accountDao" class="tx.demo.AccountDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean>
事务管理1: 编程式事务管理<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"></property> </bean> <bean id="accountService" class="tx.demo1.AccountServiceImpl"> <property name="accountDao" ref="accountDao"/> <property name="transactionTemplate" ref="transactionTemplate"/> </bean> //ServiceImpl类中: private TransactionTemplate transactionTemplate; @Override public void transfer(String from, String to, Double money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus arg0) { accountDao.outMoney(from, money); accountDao.inMoney(to, money); } }); } 声明式事务管理(配置实现,基于AOP思想)<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut expression="execution(* tx.demo2.AccountServiceImpl.*(..))" id="pointcut1"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/> </aop:config> <tx:annotation-driven transaction-manager="transactionManager"/>
以后还有的内容,再去补充!
|