• [新手课堂] Spring 学习笔记
    Spring介绍Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。WEB层:SpringMVCService层:Spring的Bean管理,声明式事务DAO层:Spring的JDBC模板,ORM模板优点:IOC:方便解耦合AOP:对程序进行扩展轻量级框架方便与其他框架整合Spring使用Spring开发包解压后的目录介绍:docs: Spring 开发规范和APIlibs: 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配置文件//class BeanFactory{    public static Object getBean(String id){        //解析XML        //反射        Class clazz=Class.forName();        return clazz.newInstance();    }}IOC XML 开发在 docs 文件中包含了 xsd-configuration.hmtl 文件。其中定义了 beans schema。    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      调用类:ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService=(UserService)applicationContext.getBean("userService");userService.save();IOC 和 DIDI 指依赖注入,其前提是必须有 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);}}在配置文件中:    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">              测试代码:@Testpublic void demo2(){//创建Spring工厂ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService=(UserService)applicationContext.getBean("userService");userService.save();}运行结果:save tony可以看到,在配置文件中配置的属性,在 Spring 管理该类的时候将其依赖的属性成功进行了设置。如果不使用依赖注入,则无法使用接口,只能使用实现类来进行设置,因为接口中没有该属性。Spring 的工厂类BeanFactory: 老版本的工厂类,在调用getBean()方法时,才会生成类的实例。ApplicationContext: 在加载配置文件的时候,就会将 Spring 管理的类都实例化。有两个实现类:ClassPathXmlApplicationContext: 加载类路径下的配置文件FileSystemXmlApplicationContext: 加载磁盘下的配置文件bean标签配置id: 唯一约束,不能出现特殊字符name: 理论上可以重复,但是开发中最好不要。可以出现特殊字符生命周期:init-method: bean被初始化的时候执行的方法destroy-method: bean被销毁的时候执行的方法作用范围:scope: bean的作用范围,有如下几种,常用的是前两种singleton: 默认使用单例模式创建prototype: 多例request: 在web项目中,spring 创建类后,将其存入到 request 范围中session: 在web项目中,spring 创建类后,将其存入到 session 范围中globalsession: 在web项目中,必须用在 porlet 环境属性注入设置构造方法方式的属性注入: Car 类在构造方法中有两个属性,分别为 name 和 price。      set 方法属性注入: Employee 类在有两个 set 方法,分别设置普通类型的 name 和引用类型的 Car (使用 ref 指向引用类型的 id 或  name)。      P名称空间的属性注入: 首先需要引入p名称空间:    //引入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">如果是普通属性:如果是引用类型:SpEL(Spring Expression Language)属性注入(Spring 3.x以上版本)      集合类型属性注入:                      qirui            baoma            benchi          多模块开发配置在加载配置文件的时候,加载多个配置文件在一个配置文件中引入多个配置文件,通过实现IOC 注解开发示例引入jar包: 除了要引入上述的四个包之外,还需要引入aop包。创建 applicationContext.xml ,使用注解开发引入 context 约束(xsd-configuration.html)    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">         组件扫描: 使用IOC注解开发,需要配置组件扫描,也就是哪些包下的类使用IOC的注解。在类上添加注解使用注解设置属性的值属性如果有set方法,将属性注入的注解添加到set方法属性没有set方法,将注解添加到属性上。@Component("UserDao")//相当于配置了一个 其id为UserDao,对应的类为该类public class UserDAOImpl implements UserDAO {@Overridepublic void save() {// TODO Auto-generated method stubSystem.out.println("save");} }注解详解@Component组件注解,用于修饰一个类,将这个类交给 Spring 管理。有三个衍生的注解,功能类似,也用来修饰类。@Controller:修饰 web 层类@Service:修饰 service 层类@Repository:修饰 dao 层类2.属性注入普通属性使用 @Value 来设置属性的值对象属性使用 @Autowired  ,这个注解是按照类型来进行属性注入的。如果希望按照 bean 的名称或id进行属性注入,需要用 @Autowired 和 @Qualifier 一起使用实际开发中,使用 @Resource(name=" ") 来进行按照对象的名称完成属性注入3.其他注解@PostConstruct 相当于 init-method,用于初始化函数的注解@PreDestroy 相当于 destroy-method,用于销毁函数的注解@Scope 作用范围的注解,常用的是默认单例,还有多例 @Scope("prototype")IOC 的 XML 和注解开发比较适用场景:XML 适用于任何场景;注解只适合自己写的类,不是自己提供的类无法添加注解。可以使用 XML 管理 bean,使用注解来进行属性注入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;}@Overridepublic 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 {@Testpublic void demo1(){UserDao userDao=new UserDaoImpl();UserDao proxy=new JDKProxy(userDao).createProxy();proxy.insert();proxy.delete();proxy.update();proxy.query();}}运行结果为:insertdelete权限校验updatequeryCglibCglib 是第三方开源代码生成类库,可以动态添加类的属性和方法。与上边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;}@Overridepublic 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与传统方式的比较获取对象方式:传统通过 new 关键字主动创建一个对象。IOC 方式中,将对象的生命周期交给 Spring 管理,直接从 Spring 获取对象。也就是控制反转————将控制权从自己手中交到了 Spring 手中。Spring 的 AOP 开发(AspectJ 的 XML 方式)AspectJ 是一个 AOP 的框架,Spring 引入 AspectJ,基于 AspectJ 进行 AOP 的开发。相关术语Joinpoint: 连接点,可以被拦截到的点。也就是可以被增强的方法都是连接点。Pointcut: 切入点,真正被拦截到的点,也就是真正被增强的方法Advice: 通知,方法层面的增强。对某个方法进行增强的方法,比如对 save 方法进行权限校验,权限校验的方法称为通知。Introduction: 引介,类层面的增强。Target: 目标,被增强的对象(类)。Weaving: 织入,将 advice 应用到 target 的过程。Proxy: 代理对象,被增强的对象。Aspect: 切面,多个通知和多个切入点的组合。使用方法引入相关包引入配置文件    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"> 编写目标类并配置:public class ProductDaoImpl implements ProductDao {@Overridepublic void save() {System.out.println("save");}@Overridepublic void update() {System.out.println("update");}@Overridepublic void find() {System.out.println("find");}@Overridepublic void delete() {System.out.println("delete");}}编写切面类,假设用于权限验证并配置public class MyAspectXML {public void checkPri(){System.out.println("check auth");}}通过AOP配置完成对目标类的增强  通知类型前置通知:在目标方法执行前操作,可以获得切入点信息public void checkPri(JoinPoint joinPoint){System.out.println("check auth "+joinPoint);}后置通知:在目标方法执行后操作,可以获得方法返回值public void writeLog(Object result){    System.out.println("writeLog "+result);}环绕通知:在目标方法执行前和后操作,可以阻止目标方法执public Object around(ProceedingJoinPoint joinPoint) throws Throwable{System.out.println("before");Object result=joinPoint.proceed();System.out.println("after");return result;}异常抛出通知:程序出现异常时操作public void afterThrowing(Throwable ex){System.out.println("exception "+ex.getMessage());}最终通知:相当于finally块,无论代码是否有异常,都会执行public void finallyFunc(){System.out.println("finally");}引介通知:不常用Spring 切入点表达式基于 execution 函数完成语法:[访问修饰符] 方法返回值 包名.类名.方法名(参数)其中任意字段可以使用*代替表示任意值Spring 的 AOP 基于 AspectJ 注解开发开发步骤引入jar包设置配置文件: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.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">编写配置目标类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注解自动代理编写切面类并配置@Aspectpublic class MyAspectAnno {@Before(value="execution(* demo1.OrderDao.save(..))")public void before(){System.out.println("before");}}注解通知类型@Before: 前置通知@AfterReturning: 后置通知@AfterReturning(value="execution(* demo1.OrderDao.save(..))",returning="result")public void after(Object result){System.out.println("after "+result);}@Around:环绕通知@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: 抛出异常@AfterThrowing(value="execution(* demo1.OrderDao.save(..))",throwing="e")public void afterThrowing(Throwable e){System.out.println("exception:"+e.getMessage();}@After: 最终通知@After(value="execution(* demo1.OrderDao.save(..))")public void after(){System.out.println("finally");}@PointCut:切入点注解@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 模板引入jar包,数据库驱动,Spring 的 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);}将连接池和模板交给 Spring 管理配置文件: 测试文件: @RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("classpath:applicationContext.xml")public class JdbcDemo2 {@Resource(name="jdbcTemplate")private JdbcTemplate jdbcTemplate;@Testpublic void demo2(){jdbcTemplate.update("insert into account values (null,?,?)", "xiaolan",1000d);}}使用开源数据库连接池使用 DBCP 的配置:使用 C3P0 的配置:引入外部属性文件首先建立外部属性文件:jdbc.driverClass=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://192.168.66.128/spring4jdbc.username=rootjdbc.password=123456然后对属性文件进行配置: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{@Overridepublic 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的事务管理事务事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。具有四个特性:原子性:事务不可分一致性:事务执行前后数据完整性保持一致隔离性:一个事务的执行不应该受到其他事务干扰持久性:一旦事务结束,数据就持久化到数据库如果不考虑隔离性会引发安全性问题:读问题:脏读:一个事务读到另一个事务未提交的数据不可重复读:一个事务读到另一个事务已经提交的 update 数据,导致一个事务中多次查询结果不一致幻读:一个事务读到另一个事务已经提交的 insert 数据,导致一个事务中多次查询结果不一致写问题:丢失更新解决读问题:设置事务隔离级别Read uncommitted: 未提交读,无法解决任何读问题Read committed: 已提交读,解决脏读问题Repeatable read: 重复读,解决脏读和不可重复读问题Serializable:序列化,解决所有读问题事务管理APIPlatformTransactionManager: 平台事务管理器这是一个接口,拥有多个不同的实现类,如 DataSourceTransactionManager 底层使用了JDBC 管理事务; HibernateTransactionManager 底层使用了 Hibernate 管理事务。TransactionDefinition: 事务定义信息用于定义事务的相关信息,如隔离级别、超时信息、传播行为、是否只读等TransactionStatus: 事务的状态用于记录在事务管理过程中,事务的状态的对象。上述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中有事务,抛出异常,也即不能用事务运行。嵌套事务PROPAGATION_NESTED:如果A有事务,按照A的事务执行,执行完成后,设置一个保存点,然后执行B的操作。如果出现异常,可以回滚到最初状态或保存点状态。实例以转账为例,业务层的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{@Overridepublic void outMoney(String from, Double money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from);}@Overridepublic 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;}@Overridepublic void transfer(String from, String to, Double money) {accountDao.outMoney(from, money);accountDao.inMoney(to, money);}}在xml中进行类的配置: 
  • [新手课堂] Spring 学习笔记
    Spring介绍Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。WEB层:SpringMVCService层:Spring的Bean管理,声明式事务DAO层:Spring的JDBC模板,ORM模板优点:IOC:方便解耦合AOP:对程序进行扩展轻量级框架方便与其他框架整合Spring使用Spring开发包解压后的目录介绍:docs: Spring 开发规范和APIlibs: 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 和 DIDI 指依赖注入,其前提是必须有 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>测试代码:@Testpublic void demo2(){//创建Spring工厂ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService=(UserService)applicationContext.getBean("userService");userService.save();}运行结果:save tony可以看到,在配置文件中配置的属性,在 Spring 管理该类的时候将其依赖的属性成功进行了设置。如果不使用依赖注入,则无法使用接口,只能使用实现类来进行设置,因为接口中没有该属性。Spring 的工厂类BeanFactory: 老版本的工厂类,在调用getBean()方法时,才会生成类的实例。ApplicationContext: 在加载配置文件的时候,就会将 Spring 管理的类都实例化。有两个实现类:ClassPathXmlApplicationContext: 加载类路径下的配置文件FileSystemXmlApplicationContext: 加载磁盘下的配置文件bean标签配置id: 唯一约束,不能出现特殊字符name: 理论上可以重复,但是开发中最好不要。可以出现特殊字符生命周期:init-method: bean被初始化的时候执行的方法destroy-method: bean被销毁的时候执行的方法作用范围:scope: bean的作用范围,有如下几种,常用的是前两种singleton: 默认使用单例模式创建prototype: 多例request: 在web项目中,spring 创建类后,将其存入到 request 范围中session: 在web项目中,spring 创建类后,将其存入到 session 范围中globalsession: 在web项目中,必须用在 porlet 环境属性注入设置构造方法方式的属性注入: Car 类在构造方法中有两个属性,分别为 name 和 price。<bean id="car" class="demo.Car">    <constructor-arg name="name" value="bmw">    <constructor-arg name="price" value="123"></bean>set 方法属性注入: Employee 类在有两个 set 方法,分别设置普通类型的 name 和引用类型的 Car (使用 ref 指向引用类型的 id 或  name)。<bean id="employee" class="demo.Employee">    <property name="name" value="xiaoming">    <property name="car" ref="car"></bean>P名称空间的属性注入: 首先需要引入p名称空间:<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>SpEL(Spring Expression Language)属性注入(Spring 3.x以上版本)<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 注解开发示例引入jar包: 除了要引入上述的四个包之外,还需要引入aop包。创建 applicationContext.xml ,使用注解开发引入 context 约束(xsd-configuration.html)<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>组件扫描: 使用IOC注解开发,需要配置组件扫描,也就是哪些包下的类使用IOC的注解。<context:component-scan base-package="demo1">在类上添加注解使用注解设置属性的值属性如果有set方法,将属性注入的注解添加到set方法属性没有set方法,将注解添加到属性上。@Component("UserDao")//相当于配置了一个<bean> 其id为UserDao,对应的类为该类public class UserDAOImpl implements UserDAO {@Overridepublic void save() {// TODO Auto-generated method stubSystem.out.println("save");} }注解详解@Component组件注解,用于修饰一个类,将这个类交给 Spring 管理。有三个衍生的注解,功能类似,也用来修饰类。@Controller:修饰 web 层类@Service:修饰 service 层类@Repository:修饰 dao 层类2.属性注入普通属性使用 @Value 来设置属性的值对象属性使用 @Autowired  ,这个注解是按照类型来进行属性注入的。如果希望按照 bean 的名称或id进行属性注入,需要用 @Autowired 和 @Qualifier 一起使用实际开发中,使用 @Resource(name=" ") 来进行按照对象的名称完成属性注入3.其他注解@PostConstruct 相当于 init-method,用于初始化函数的注解@PreDestroy 相当于 destroy-method,用于销毁函数的注解@Scope 作用范围的注解,常用的是默认单例,还有多例 @Scope("prototype")IOC 的 XML 和注解开发比较适用场景:XML 适用于任何场景;注解只适合自己写的类,不是自己提供的类无法添加注解。可以使用 XML 管理 bean,使用注解来进行属性注入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;}@Overridepublic 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 {@Testpublic void demo1(){UserDao userDao=new UserDaoImpl();UserDao proxy=new JDKProxy(userDao).createProxy();proxy.insert();proxy.delete();proxy.update();proxy.query();}}运行结果为:insertdelete权限校验updatequeryCglibCglib 是第三方开源代码生成类库,可以动态添加类的属性和方法。与上边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;}@Overridepublic 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与传统方式的比较获取对象方式:传统通过 new 关键字主动创建一个对象。IOC 方式中,将对象的生命周期交给 Spring 管理,直接从 Spring 获取对象。也就是控制反转————将控制权从自己手中交到了 Spring 手中。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 {@Overridepublic void save() {System.out.println("save");}@Overridepublic void update() {System.out.println("update");}@Overridepublic void find() {System.out.println("find");}@Overridepublic 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配置完成对目标类的增强<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());}最终通知:相当于finally块,无论代码是否有异常,都会执行<aop:after method="finallyFunc" pointcut-ref="pointcut4"/>public void finallyFunc(){System.out.println("finally");}引介通知:不常用Spring 切入点表达式基于 execution 函数完成语法:[访问修饰符] 方法返回值 包名.类名.方法名(参数)其中任意字段可以使用*代替表示任意值Spring 的 AOP 基于 AspectJ 注解开发开发步骤引入jar包设置配置文件:<?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.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://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注解自动代理<aop:aspectj-autoproxy/>编写切面类并配置@Aspectpublic 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:环绕通知@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: 抛出异常@AfterThrowing(value="execution(* demo1.OrderDao.save(..))",throwing="e")public void afterThrowing(Throwable e){System.out.println("exception:"+e.getMessage();}@After: 最终通知@After(value="execution(* demo1.OrderDao.save(..))")public void after(){System.out.println("finally");}@PointCut:切入点注解@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 模板引入jar包,数据库驱动,Spring 的 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);}将连接池和模板交给 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:///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;@Testpublic void demo2(){jdbcTemplate.update("insert into account values (null,?,?)", "xiaolan",1000d);}}使用开源数据库连接池使用 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://192.168.66.128/spring4"></property><property name="username" value="root"></property><property name="password" value="123456"></property>使用 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://192.168.66.128/spring4"></property><property name="user" value="root"></property><property name="password" value="123456"></property></bean>引入外部属性文件首先建立外部属性文件:jdbc.driverClass=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://192.168.66.128/spring4jdbc.username=rootjdbc.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>{@Overridepublic 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的事务管理事务事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。具有四个特性:原子性:事务不可分一致性:事务执行前后数据完整性保持一致隔离性:一个事务的执行不应该受到其他事务干扰持久性:一旦事务结束,数据就持久化到数据库如果不考虑隔离性会引发安全性问题:读问题:脏读:一个事务读到另一个事务未提交的数据不可重复读:一个事务读到另一个事务已经提交的 update 数据,导致一个事务中多次查询结果不一致幻读:一个事务读到另一个事务已经提交的 insert 数据,导致一个事务中多次查询结果不一致写问题:丢失更新解决读问题:设置事务隔离级别Read uncommitted: 未提交读,无法解决任何读问题Read committed: 已提交读,解决脏读问题Repeatable read: 重复读,解决脏读和不可重复读问题Serializable:序列化,解决所有读问题事务管理APIPlatformTransactionManager: 平台事务管理器这是一个接口,拥有多个不同的实现类,如 DataSourceTransactionManager 底层使用了JDBC 管理事务; HibernateTransactionManager 底层使用了 Hibernate 管理事务。TransactionDefinition: 事务定义信息用于定义事务的相关信息,如隔离级别、超时信息、传播行为、是否只读等TransactionStatus: 事务的状态用于记录在事务管理过程中,事务的状态的对象。上述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中有事务,抛出异常,也即不能用事务运行。嵌套事务PROPAGATION_NESTED:如果A有事务,按照A的事务执行,执行完成后,设置一个保存点,然后执行B的操作。如果出现异常,可以回滚到最初状态或保存点状态。实例以转账为例,业务层的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{@Overridepublic void outMoney(String from, Double money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from);}@Overridepublic 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;}@Overridepublic 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> 
  • [新手课堂] Spring是什么?
    一、Spring是什么呢???1.Spring 是目前主流的 Java Web 开发框架,是 Java 世界最为成功的框架。该框架是一个轻量级的开源框架,具有很高的凝聚力和吸引力。2.Spring框架目的就是为解决企业应用开发的复杂性。说白就是使得开发企业应用变得更容易。二、Spring的结构又是怎样的呢???Spring 框架采用分层的理念,根据功能的不同划分成了多个模块,这些模块大体可分为 Data Access/Integration(数据访问与集成)、Web、AOP、Aspects、Instrumentation(检测)、Messaging(消息处理)、Core Container(核心容器)和 Test。这些模块可以满足一切企业级应用开发的需求,在开发过程中可以根据需求有选择性地使用所需要的模块。如下图所示。这里我就不详细介绍各个模块了,感兴趣的可以浏览Spring官网(这句话我老师常说 ^ ~ ^)三、初步了解Spring 的核心:控制反转(IoC)和面向切面编程(AOP);IOC(控制反转)(这大哥比较晦涩难懂)控制反转时一种通过描述(在java中可以时XML或者时注解)并通过第三方去产生或获取特定对象的方式。(意思是我需要某个对象,我自己不new一个,我描述一下需要的对象,通过别人给我。Ioc不是一种技术,而是一种很优美的思想!!!)spring Ioc 容器就完美的应用了这种思想!!!IoC 容器是 Spring 的核心,也可以称为 Spring 容器。Spring 通过 IoC 容器来管理对象的实例化和初始化,以及对象从创建到销毁的整个生命周期。Spring 中使用的对象都由 IoC 容器管理,不需要我们手动使用 new 运算符创建对象。由 IoC 容器管理的对象称为 Spring Bean,Spring Bean 就是 Java 对象,和使用 new 运算符创建的对象没有区别。Spring 通过读取 XML 或 Java 注解中的信息来获取哪些对象需要实例化。Aop(面向切面编程)AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,和 OOP(面向对象编程)类似,也是一种编程思想。AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。AOP 就是代理模式的典型应用。四、说说Spring的优点;Spring 框架具有以下几个特点。1)方便解耦,简化开发Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理(Ioc容器)。2)方便集成各种优秀框架Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。3)降低 Java EE API 的使用难度Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。4)方便程序的测试Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。5)AOP 编程的支持Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。6)声明式事务的支持只需要通过配置就可以完成对事务的管理,而无须手动编程。
  • [新手课堂] Spring是什么?
    一、Spring是什么呢???1.Spring 是目前主流的 Java Web 开发框架,是 Java 世界最为成功的框架。该框架是一个轻量级的开源框架,具有很高的凝聚力和吸引力。2.Spring框架目的就是为解决企业应用开发的复杂性。说白就是使得开发企业应用变得更容易。二、Spring的结构又是怎样的呢???Spring 框架采用分层的理念,根据功能的不同划分成了多个模块,这些模块大体可分为 Data Access/Integration(数据访问与集成)、Web、AOP、Aspects、Instrumentation(检测)、Messaging(消息处理)、Core Container(核心容器)和 Test。这些模块可以满足一切企业级应用开发的需求,在开发过程中可以根据需求有选择性地使用所需要的模块。如下图所示。这里我就不详细介绍各个模块了,感兴趣的可以浏览Spring官网(这句话我老师常说 ^ ~ ^)三、初步了解Spring 的核心:控制反转(IoC)和面向切面编程(AOP);IOC(控制反转)(这大哥比较晦涩难懂)控制反转时一种通过描述(在java中可以时XML或者时注解)并通过第三方去产生或获取特定对象的方式。(意思是我需要某个对象,我自己不new一个,我描述一下需要的对象,通过别人给我。Ioc不是一种技术,而是一种很优美的思想!!!)spring Ioc 容器就完美的应用了这种思想!!!IoC 容器是 Spring 的核心,也可以称为 Spring 容器。Spring 通过 IoC 容器来管理对象的实例化和初始化,以及对象从创建到销毁的整个生命周期。Spring 中使用的对象都由 IoC 容器管理,不需要我们手动使用 new 运算符创建对象。由 IoC 容器管理的对象称为 Spring Bean,Spring Bean 就是 Java 对象,和使用 new 运算符创建的对象没有区别。Spring 通过读取 XML 或 Java 注解中的信息来获取哪些对象需要实例化。Aop(面向切面编程)AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,和 OOP(面向对象编程)类似,也是一种编程思想。AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。AOP 就是代理模式的典型应用。四、说说Spring的优点;Spring 框架具有以下几个特点。1)方便解耦,简化开发Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理(Ioc容器)。2)方便集成各种优秀框架Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。3)降低 Java EE API 的使用难度Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。4)方便程序的测试Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。5)AOP 编程的支持Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。6)声明式事务的支持只需要通过配置就可以完成对事务的管理,而无须手动编程。
  • [新手课堂] Spring是什么?
    一、Spring是什么呢???1.Spring 是目前主流的 Java Web 开发框架,是 Java 世界最为成功的框架。该框架是一个轻量级的开源框架,具有很高的凝聚力和吸引力。2.Spring框架目的就是为解决企业应用开发的复杂性。说白就是使得开发企业应用变得更容易。二、Spring的结构又是怎样的呢???Spring 框架采用分层的理念,根据功能的不同划分成了多个模块,这些模块大体可分为 Data Access/Integration(数据访问与集成)、Web、AOP、Aspects、Instrumentation(检测)、Messaging(消息处理)、Core Container(核心容器)和 Test。这些模块可以满足一切企业级应用开发的需求,在开发过程中可以根据需求有选择性地使用所需要的模块。如下图所示。这里我就不详细介绍各个模块了,感兴趣的可以浏览Spring官网(这句话我老师常说 ^ ~ ^)三、初步了解Spring 的核心:控制反转(IoC)和面向切面编程(AOP);IOC(控制反转)(这大哥比较晦涩难懂)控制反转时一种通过描述(在java中可以时XML或者时注解)并通过第三方去产生或获取特定对象的方式。(意思是我需要某个对象,我自己不new一个,我描述一下需要的对象,通过别人给我。Ioc不是一种技术,而是一种很优美的思想!!!)spring Ioc 容器就完美的应用了这种思想!!!IoC 容器是 Spring 的核心,也可以称为 Spring 容器。Spring 通过 IoC 容器来管理对象的实例化和初始化,以及对象从创建到销毁的整个生命周期。Spring 中使用的对象都由 IoC 容器管理,不需要我们手动使用 new 运算符创建对象。由 IoC 容器管理的对象称为 Spring Bean,Spring Bean 就是 Java 对象,和使用 new 运算符创建的对象没有区别。Spring 通过读取 XML 或 Java 注解中的信息来获取哪些对象需要实例化。Aop(面向切面编程)AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,和 OOP(面向对象编程)类似,也是一种编程思想。AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。AOP 就是代理模式的典型应用。四、说说Spring的优点;Spring 框架具有以下几个特点。1)方便解耦,简化开发Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理(Ioc容器)。2)方便集成各种优秀框架Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。3)降低 Java EE API 的使用难度Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装,使这些 API 应用的难度大大降低。4)方便程序的测试Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。5)AOP 编程的支持Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。6)声明式事务的支持只需要通过配置就可以完成对事务的管理,而无须手动编程。
  • [新手课堂] Spring 学习笔记
    Spring介绍Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。WEB层:SpringMVCService层:Spring的Bean管理,声明式事务DAO层:Spring的JDBC模板,ORM模板优点:IOC:方便解耦合AOP:对程序进行扩展轻量级框架方便与其他框架整合Spring使用Spring开发包解压后的目录介绍:docs: Spring 开发规范和APIlibs: 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 和 DIDI 指依赖注入,其前提是必须有 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>测试代码:@Testpublic void demo2(){//创建Spring工厂ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService=(UserService)applicationContext.getBean("userService");userService.save();}运行结果:save tony可以看到,在配置文件中配置的属性,在 Spring 管理该类的时候将其依赖的属性成功进行了设置。如果不使用依赖注入,则无法使用接口,只能使用实现类来进行设置,因为接口中没有该属性。Spring 的工厂类BeanFactory: 老版本的工厂类,在调用getBean()方法时,才会生成类的实例。ApplicationContext: 在加载配置文件的时候,就会将 Spring 管理的类都实例化。有两个实现类:ClassPathXmlApplicationContext: 加载类路径下的配置文件FileSystemXmlApplicationContext: 加载磁盘下的配置文件bean标签配置id: 唯一约束,不能出现特殊字符name: 理论上可以重复,但是开发中最好不要。可以出现特殊字符生命周期:init-method: bean被初始化的时候执行的方法destroy-method: bean被销毁的时候执行的方法作用范围:scope: bean的作用范围,有如下几种,常用的是前两种singleton: 默认使用单例模式创建prototype: 多例request: 在web项目中,spring 创建类后,将其存入到 request 范围中session: 在web项目中,spring 创建类后,将其存入到 session 范围中globalsession: 在web项目中,必须用在 porlet 环境属性注入设置构造方法方式的属性注入: Car 类在构造方法中有两个属性,分别为 name 和 price。<bean id="car" class="demo.Car">    <constructor-arg name="name" value="bmw">    <constructor-arg name="price" value="123"></bean>set 方法属性注入: Employee 类在有两个 set 方法,分别设置普通类型的 name 和引用类型的 Car (使用 ref 指向引用类型的 id 或  name)。<bean id="employee" class="demo.Employee">    <property name="name" value="xiaoming">    <property name="car" ref="car"></bean>P名称空间的属性注入: 首先需要引入p名称空间:<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>SpEL(Spring Expression Language)属性注入(Spring 3.x以上版本)<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 注解开发示例引入jar包: 除了要引入上述的四个包之外,还需要引入aop包。创建 applicationContext.xml ,使用注解开发引入 context 约束(xsd-configuration.html)<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>组件扫描: 使用IOC注解开发,需要配置组件扫描,也就是哪些包下的类使用IOC的注解。<context:component-scan base-package="demo1">在类上添加注解使用注解设置属性的值属性如果有set方法,将属性注入的注解添加到set方法属性没有set方法,将注解添加到属性上。@Component("UserDao")//相当于配置了一个<bean> 其id为UserDao,对应的类为该类public class UserDAOImpl implements UserDAO {@Overridepublic void save() {// TODO Auto-generated method stubSystem.out.println("save");} }注解详解@Component组件注解,用于修饰一个类,将这个类交给 Spring 管理。有三个衍生的注解,功能类似,也用来修饰类。@Controller:修饰 web 层类@Service:修饰 service 层类@Repository:修饰 dao 层类2.属性注入普通属性使用 @Value 来设置属性的值对象属性使用 @Autowired  ,这个注解是按照类型来进行属性注入的。如果希望按照 bean 的名称或id进行属性注入,需要用 @Autowired 和 @Qualifier 一起使用实际开发中,使用 @Resource(name=" ") 来进行按照对象的名称完成属性注入3.其他注解@PostConstruct 相当于 init-method,用于初始化函数的注解@PreDestroy 相当于 destroy-method,用于销毁函数的注解@Scope 作用范围的注解,常用的是默认单例,还有多例 @Scope("prototype")IOC 的 XML 和注解开发比较适用场景:XML 适用于任何场景;注解只适合自己写的类,不是自己提供的类无法添加注解。可以使用 XML 管理 bean,使用注解来进行属性注入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;}@Overridepublic 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 {@Testpublic void demo1(){UserDao userDao=new UserDaoImpl();UserDao proxy=new JDKProxy(userDao).createProxy();proxy.insert();proxy.delete();proxy.update();proxy.query();}}运行结果为:insertdelete权限校验updatequeryCglibCglib 是第三方开源代码生成类库,可以动态添加类的属性和方法。与上边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;}@Overridepublic 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与传统方式的比较获取对象方式:传统通过 new 关键字主动创建一个对象。IOC 方式中,将对象的生命周期交给 Spring 管理,直接从 Spring 获取对象。也就是控制反转————将控制权从自己手中交到了 Spring 手中。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 {@Overridepublic void save() {System.out.println("save");}@Overridepublic void update() {System.out.println("update");}@Overridepublic void find() {System.out.println("find");}@Overridepublic 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配置完成对目标类的增强<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());}最终通知:相当于finally块,无论代码是否有异常,都会执行<aop:after method="finallyFunc" pointcut-ref="pointcut4"/>public void finallyFunc(){System.out.println("finally");}引介通知:不常用Spring 切入点表达式基于 execution 函数完成语法:[访问修饰符] 方法返回值 包名.类名.方法名(参数)其中任意字段可以使用*代替表示任意值Spring 的 AOP 基于 AspectJ 注解开发开发步骤引入jar包设置配置文件:<?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.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://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注解自动代理<aop:aspectj-autoproxy/>编写切面类并配置@Aspectpublic 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:环绕通知@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: 抛出异常@AfterThrowing(value="execution(* demo1.OrderDao.save(..))",throwing="e")public void afterThrowing(Throwable e){System.out.println("exception:"+e.getMessage();}@After: 最终通知@After(value="execution(* demo1.OrderDao.save(..))")public void after(){System.out.println("finally");}@PointCut:切入点注解@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 模板引入jar包,数据库驱动,Spring 的 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);}将连接池和模板交给 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:///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;@Testpublic void demo2(){jdbcTemplate.update("insert into account values (null,?,?)", "xiaolan",1000d);}}使用开源数据库连接池使用 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://192.168.66.128/spring4"></property><property name="username" value="root"></property><property name="password" value="123456"></property>使用 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://192.168.66.128/spring4"></property><property name="user" value="root"></property><property name="password" value="123456"></property></bean>引入外部属性文件首先建立外部属性文件:jdbc.driverClass=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://192.168.66.128/spring4jdbc.username=rootjdbc.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>{@Overridepublic 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的事务管理事务事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。具有四个特性:原子性:事务不可分一致性:事务执行前后数据完整性保持一致隔离性:一个事务的执行不应该受到其他事务干扰持久性:一旦事务结束,数据就持久化到数据库如果不考虑隔离性会引发安全性问题:读问题:脏读:一个事务读到另一个事务未提交的数据不可重复读:一个事务读到另一个事务已经提交的 update 数据,导致一个事务中多次查询结果不一致幻读:一个事务读到另一个事务已经提交的 insert 数据,导致一个事务中多次查询结果不一致写问题:丢失更新解决读问题:设置事务隔离级别Read uncommitted: 未提交读,无法解决任何读问题Read committed: 已提交读,解决脏读问题Repeatable read: 重复读,解决脏读和不可重复读问题Serializable:序列化,解决所有读问题事务管理APIPlatformTransactionManager: 平台事务管理器这是一个接口,拥有多个不同的实现类,如 DataSourceTransactionManager 底层使用了JDBC 管理事务; HibernateTransactionManager 底层使用了 Hibernate 管理事务。TransactionDefinition: 事务定义信息用于定义事务的相关信息,如隔离级别、超时信息、传播行为、是否只读等TransactionStatus: 事务的状态用于记录在事务管理过程中,事务的状态的对象。上述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中有事务,抛出异常,也即不能用事务运行。嵌套事务PROPAGATION_NESTED:如果A有事务,按照A的事务执行,执行完成后,设置一个保存点,然后执行B的操作。如果出现异常,可以回滚到最初状态或保存点状态。实例以转账为例,业务层的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{@Overridepublic void outMoney(String from, Double money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from);}@Overridepublic 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;}@Overridepublic 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> 
  • [新手课堂] Spring 学习笔记
    Spring介绍Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。WEB层:SpringMVCService层:Spring的Bean管理,声明式事务DAO层:Spring的JDBC模板,ORM模板优点:IOC:方便解耦合AOP:对程序进行扩展轻量级框架方便与其他框架整合Spring使用Spring开发包解压后的目录介绍:docs: Spring 开发规范和APIlibs: 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 和 DIDI 指依赖注入,其前提是必须有 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>测试代码:@Testpublic void demo2(){//创建Spring工厂ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml");UserService userService=(UserService)applicationContext.getBean("userService");userService.save();}运行结果:save tony可以看到,在配置文件中配置的属性,在 Spring 管理该类的时候将其依赖的属性成功进行了设置。如果不使用依赖注入,则无法使用接口,只能使用实现类来进行设置,因为接口中没有该属性。Spring 的工厂类BeanFactory: 老版本的工厂类,在调用getBean()方法时,才会生成类的实例。ApplicationContext: 在加载配置文件的时候,就会将 Spring 管理的类都实例化。有两个实现类:ClassPathXmlApplicationContext: 加载类路径下的配置文件FileSystemXmlApplicationContext: 加载磁盘下的配置文件bean标签配置id: 唯一约束,不能出现特殊字符name: 理论上可以重复,但是开发中最好不要。可以出现特殊字符生命周期:init-method: bean被初始化的时候执行的方法destroy-method: bean被销毁的时候执行的方法作用范围:scope: bean的作用范围,有如下几种,常用的是前两种singleton: 默认使用单例模式创建prototype: 多例request: 在web项目中,spring 创建类后,将其存入到 request 范围中session: 在web项目中,spring 创建类后,将其存入到 session 范围中globalsession: 在web项目中,必须用在 porlet 环境属性注入设置构造方法方式的属性注入: Car 类在构造方法中有两个属性,分别为 name 和 price。<bean id="car" class="demo.Car">    <constructor-arg name="name" value="bmw">    <constructor-arg name="price" value="123"></bean>set 方法属性注入: Employee 类在有两个 set 方法,分别设置普通类型的 name 和引用类型的 Car (使用 ref 指向引用类型的 id 或  name)。<bean id="employee" class="demo.Employee">    <property name="name" value="xiaoming">    <property name="car" ref="car"></bean>P名称空间的属性注入: 首先需要引入p名称空间:<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>SpEL(Spring Expression Language)属性注入(Spring 3.x以上版本)<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 注解开发示例引入jar包: 除了要引入上述的四个包之外,还需要引入aop包。创建 applicationContext.xml ,使用注解开发引入 context 约束(xsd-configuration.html)<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>组件扫描: 使用IOC注解开发,需要配置组件扫描,也就是哪些包下的类使用IOC的注解。<context:component-scan base-package="demo1">在类上添加注解使用注解设置属性的值属性如果有set方法,将属性注入的注解添加到set方法属性没有set方法,将注解添加到属性上。@Component("UserDao")//相当于配置了一个<bean> 其id为UserDao,对应的类为该类public class UserDAOImpl implements UserDAO {@Overridepublic void save() {// TODO Auto-generated method stubSystem.out.println("save");} }注解详解@Component组件注解,用于修饰一个类,将这个类交给 Spring 管理。有三个衍生的注解,功能类似,也用来修饰类。@Controller:修饰 web 层类@Service:修饰 service 层类@Repository:修饰 dao 层类2.属性注入普通属性使用 @Value 来设置属性的值对象属性使用 @Autowired  ,这个注解是按照类型来进行属性注入的。如果希望按照 bean 的名称或id进行属性注入,需要用 @Autowired 和 @Qualifier 一起使用实际开发中,使用 @Resource(name=" ") 来进行按照对象的名称完成属性注入3.其他注解@PostConstruct 相当于 init-method,用于初始化函数的注解@PreDestroy 相当于 destroy-method,用于销毁函数的注解@Scope 作用范围的注解,常用的是默认单例,还有多例 @Scope("prototype")IOC 的 XML 和注解开发比较适用场景:XML 适用于任何场景;注解只适合自己写的类,不是自己提供的类无法添加注解。可以使用 XML 管理 bean,使用注解来进行属性注入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;}@Overridepublic 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 {@Testpublic void demo1(){UserDao userDao=new UserDaoImpl();UserDao proxy=new JDKProxy(userDao).createProxy();proxy.insert();proxy.delete();proxy.update();proxy.query();}}运行结果为:insertdelete权限校验updatequeryCglibCglib 是第三方开源代码生成类库,可以动态添加类的属性和方法。与上边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;}@Overridepublic 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与传统方式的比较获取对象方式:传统通过 new 关键字主动创建一个对象。IOC 方式中,将对象的生命周期交给 Spring 管理,直接从 Spring 获取对象。也就是控制反转————将控制权从自己手中交到了 Spring 手中。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 {@Overridepublic void save() {System.out.println("save");}@Overridepublic void update() {System.out.println("update");}@Overridepublic void find() {System.out.println("find");}@Overridepublic 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配置完成对目标类的增强<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());}最终通知:相当于finally块,无论代码是否有异常,都会执行<aop:after method="finallyFunc" pointcut-ref="pointcut4"/>public void finallyFunc(){System.out.println("finally");}引介通知:不常用Spring 切入点表达式基于 execution 函数完成语法:[访问修饰符] 方法返回值 包名.类名.方法名(参数)其中任意字段可以使用*代替表示任意值Spring 的 AOP 基于 AspectJ 注解开发开发步骤引入jar包设置配置文件:<?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.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://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注解自动代理<aop:aspectj-autoproxy/>编写切面类并配置@Aspectpublic 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:环绕通知@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: 抛出异常@AfterThrowing(value="execution(* demo1.OrderDao.save(..))",throwing="e")public void afterThrowing(Throwable e){System.out.println("exception:"+e.getMessage();}@After: 最终通知@After(value="execution(* demo1.OrderDao.save(..))")public void after(){System.out.println("finally");}@PointCut:切入点注解@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 模板引入jar包,数据库驱动,Spring 的 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);}将连接池和模板交给 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:///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;@Testpublic void demo2(){jdbcTemplate.update("insert into account values (null,?,?)", "xiaolan",1000d);}}使用开源数据库连接池使用 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://192.168.66.128/spring4"></property><property name="username" value="root"></property><property name="password" value="123456"></property>使用 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://192.168.66.128/spring4"></property><property name="user" value="root"></property><property name="password" value="123456"></property></bean>引入外部属性文件首先建立外部属性文件:jdbc.driverClass=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://192.168.66.128/spring4jdbc.username=rootjdbc.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>{@Overridepublic 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的事务管理事务事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。具有四个特性:原子性:事务不可分一致性:事务执行前后数据完整性保持一致隔离性:一个事务的执行不应该受到其他事务干扰持久性:一旦事务结束,数据就持久化到数据库如果不考虑隔离性会引发安全性问题:读问题:脏读:一个事务读到另一个事务未提交的数据不可重复读:一个事务读到另一个事务已经提交的 update 数据,导致一个事务中多次查询结果不一致幻读:一个事务读到另一个事务已经提交的 insert 数据,导致一个事务中多次查询结果不一致写问题:丢失更新解决读问题:设置事务隔离级别Read uncommitted: 未提交读,无法解决任何读问题Read committed: 已提交读,解决脏读问题Repeatable read: 重复读,解决脏读和不可重复读问题Serializable:序列化,解决所有读问题事务管理APIPlatformTransactionManager: 平台事务管理器这是一个接口,拥有多个不同的实现类,如 DataSourceTransactionManager 底层使用了JDBC 管理事务; HibernateTransactionManager 底层使用了 Hibernate 管理事务。TransactionDefinition: 事务定义信息用于定义事务的相关信息,如隔离级别、超时信息、传播行为、是否只读等TransactionStatus: 事务的状态用于记录在事务管理过程中,事务的状态的对象。上述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中有事务,抛出异常,也即不能用事务运行。嵌套事务PROPAGATION_NESTED:如果A有事务,按照A的事务执行,执行完成后,设置一个保存点,然后执行B的操作。如果出现异常,可以回滚到最初状态或保存点状态。实例以转账为例,业务层的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{@Overridepublic void outMoney(String from, Double money) { this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,from);}@Overridepublic 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;}@Overridepublic 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> 
  • [其他] Spring三大核心思想之AOP(面向切面编程)
    一、什么是AOP(面向切面编程)?AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式 和运行期 动态代理 实现程序功能的统一维护的一种技术。AOP (面向切面编程)是 OOP(面向对象) 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程 的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。二、AOP 的作用及其优势作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强优势:减少重复代码,提高开发效率,并且便于维护三、AOP 的底层实现实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现 的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。动态代理的作用:    1)在目标类源代码不改变的情况下,增加功能。    2)减少代码的重复    3)专注业务逻辑代码    4)解耦合,让你的业务功能和日志,事务非业务功能分离。代理设计模式详细笔记 :代理模式详细笔记,想学好Spring的AOP思想先理解代理模式java设计模式之代理设计模式笔记详细内容:代理模式是结构型模式其中的一种现在开发中存在的问题什么是代理模式,为什么需要使用代理模式静态代理及实现什么是动态代理JDK 动态代理和cglib动态代理的使用及区别三种动态代理的对比及优缺点代理模式的使用场景四、AOP 相关概念Spring 的 AOP 实现底层就是对动态代理的代码进行了封装 ,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。在正式讲解 AOP 的操作之前,我们必须理解 AOP 的相关术语,常用的术语如下:1、切面(Aspect)一个切面就是一个代理对象= 要为那些类生成代理+要添加的额外功能是那些2、切入点(pointcut):将来要为那些类生成代理对象3、通知/ 增强(advice):就是要添加的额外功能生活案例:给面包之间涂果酱注意:切面(Aspect)=切入点(pointcut)+通知点(advice,额外功能)4、AOP中的(面向切面编程)通知前置通知:目标方法之前的额外功能 MethodBeforeAdvice环绕通知:它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式 MethodInterceptor(Interceptor [ɪntəˈsɛptə]拦截器)后置通知:目标方法之后的额外功能 AfterReturningAdvice(returning[rɪˈtɜːrnɪŋ])异常通知:执行异常的额外功能 ThrowsAdvice最终通知:一定执行的额外功能5、Target(目标对象):代理的目标对象6、代理(Proxy[ˈprɑːksi]):一个类被AOP注入增强后,就产生一个结果代理类7、连接点(joinPoint):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法(额外功能),因为spring只支持方法类型的连接点五、切面编程步骤1、五种通知类AOP 切面=切入点+通知前置通知:MethodBeforeAdvice后置通知:AfterReturnAdvice环绕通知:MethodInterceptor异常通知:ThrowsAdvice(throw [θroʊz])最终通知通知的配置语法:<aop : 通知类型 method=“切面类中方法名” pointcut=“切点表达式"> </aop:通知类型>1切点表达式的抽取当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。<aop:config>    <!--引用myAspect的Bean为切面对象-->    <aop:aspect ref="myAspect">        <aop:pointcut id="myPointcut" expression="execution(* com.tjcu.aop.*.*(..))"/>        <aop:before method="before" pointcut-ref="myPointcut"></aop:before>    </aop:aspect></aop:config>aop织入的配置<aop:config>    <aop:aspect ref=“切面类”>        <aop:before method=“通知方法名称” pointcut=“切点表达式"></aop:before>    </aop:aspect></aop:config>切点表达式的写法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))12、AOP的开发步骤1、开发目标类2、开发通知类,确定额外功能3、管理通知类4、配置切入点 确定要为那些类添加额外功能5、将目标类和切面类的对象创建权交给 spring6、组装切面 切入点+通知(额外功能)3、AOP编程所需要的依赖引入依赖   spring-aop   spring-expression   spring-aspects六、AOP实现前置通知案例1、目标接口类public interface CityService {    public void login();    public  void add(String name);}2、目标实现类(核心功能)/** * @author 王恒杰 * @Description:目标对象 */public class CityServiceImpl implements CityService{    @Override    public void login() {        //前置通知:System.out.println("嘻嘻哈哈");        //执行核心的业务逻辑 调用Dao        System.out.println("登录调用Dao");    }    @Override    public void add(String name) {        //前置通知:System.out.println("嘻嘻哈哈");        //执行核心的业务逻辑 调用Dao        System.out.println("添加调用Dao");    }}3、前置通知(额外功能)动态代理代码/** * @author 王恒杰 * @Description: 通知类。额外功能 */public class MyBeforeAdvice implements MethodBeforeAdvice {    /**     * 额外功能书写的方法     * 参数1:代理对象当前调用的方法     * 参数2:当前代理对象调用的方法的参数     * 参数3:目标对象(被代理的对象)     * @param method     * @param objects     * @param o     * @throws Throwable     */    @Override    public void before(Method method, Object[] objects, Object o) throws Throwable {     System.out.println("嘻嘻哈哈");    }}4、将目标类和切面类的对象创建权交给 spring<!--管理目标对象-->    <bean class="before.CityServiceImpl" id="cityService"></bean>    <!--管理通知类 动态代理实现AOP-->    <bean id="myBeforeAdvice" class="before.MyBeforeAdvice"></bean>5、在 spring.xml 中配置织入关系(前置功能)<!--组装切面-->    <aop:config>    <!--配置切入点    id:切入点的唯一标识    expression:切入点表达式,为那些类添加额外功能     execution() 切入点表达式的一种 精确到要添加到额外功能的方法     *:通配     空格:     before.CityServiceImpl.*:所有方法     (..):所有参数    -->        <aop:pointcut id="pc1" expression="execution(* before.CityServiceImpl.*(..))"/>    <!--组装切面=切入点+通知      advice-ref:通知的id      pointcut-ref:切入点的id    -->        <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="pc1"></aop:advisor>    </aop:config>6、测试代码   @Test    public void testMethodBeforeAdvice() {        //启动工厂        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("before/spring.xml");        //获取组件 目标对象就是代理对象        CityService cityService = (CityService) ctx.getBean("cityService");                //目标对象就是代理对象  class com.sun.proxy.$Proxy4        System.out.println(cityService.getClass());                    //调用方法,通过代理类调用目标类        cityService.add("123");    }注意: 获取组件时,目标对象就是代理对象七、Spring中的环绕通知案例1、dao层接口public interface StudentDao {    /**     * 登录     * @param name     */    public void login(String name);    /**     * 分页     * @param name     * @return     */    public String pageShow(String name);}2、dao层实现类public class StudentDaoImpl implements StudentDao{    @Override    public void login(String name) {        //循环10000次        for (int i = 0; i < 10000; i++) {        }        System.out.println("数据库实现登录");    }    @Override    public String pageShow(String name) {        //循环10000次        for (int i = 0; i < 10000; i++) {        }        System.out.println("数据库实现分页");        return name;    }}3、目标接口类public interface StudentService {    /**     * 登录     * @param name     */    public void login(String name);    /**     * 分页     * @param name     * @return     */    public String pageShow(String name);}4、目标实现类public class StudentServiceImpl implements StudentService{    private StudentDao studentDao;    public void setStudentDao(StudentDao studentDao) {        this.studentDao = studentDao;    }    @Override    public void login(String name) {        System.out.println("登录日志");      studentDao.login(name);    }    @Override    public String pageShow(String name) {        System.out.println("分页日志");        String s = studentDao.pageShow(name);        return s;    }}5、环绕通知核心方法: Object proceed = methodInvocation.proceed(); 放行public class StudentAroundAdvice implements MethodInterceptor {    /**     * 参数 内部封装者当前的代理对象 方法的参数,执行方法等     *     * @param methodInvocation     * @return     * @throws Throwable     */    @Override    public Object invoke(MethodInvocation methodInvocation) throws Throwable {        //1.控制事务        System.out.println("控制事务");        //method Invocation 方法调用        System.out.println("当前调用方法的名字" + methodInvocation.getMethod().getName());        //arguments参数 methodInvocation:方法调用        System.out.println("当前的参数为:" + methodInvocation.getArguments()[0]);        System.out.println("--------------");        //记录当前的时间 单位毫秒        long begin = System.currentTimeMillis();        System.out.println("调用查询的数据库");        //放行,执行目标方法 proceed:继续做proceed        Object proceed = methodInvocation.proceed();        //记录结束的时间 单位毫秒        long end = System.currentTimeMillis();        System.out.println("dao执行所用时间" + (end - begin));        return proceed;    }}6、将目标类和切面类的对象创建权交给 spring  <!--管理dao组件-->    <bean id="studentDao" class="com.tjcu.dao.StudentDaoImpl"></bean>    <!--管理Service组件/目标对象-->    <bean id="studentService" class="com.tjcu.service.StudentServiceImpl">        <!--注入值-->        <property name="studentDao" ref="studentDao"></property>    </bean>    <!--管理通知组件-->    <bean id="studentAroundAdvice" class="com.tjcu.advice.StudentAroundAdvice"></bean>7、在 applicationContext.xml 中配置织入关系,aop相关配置 <!--aop相关配置  切面=切点+环绕通知-->    <aop:config>        <!--切入点 execution:[eksɪˈkjuːʃn]执行-->        <aop:pointcut id="pointcut" expression="execution(* com.tjcu.service.StudentServiceImpl.*(..))"/>        <!--组装切面  advisor[ [ædˈvaɪzər]]:顾问  advice-ref:通知  pointcut-ref:切入点-->        <aop:advisor advice-ref="studentAroundAdvice" pointcut-ref="pointcut"></aop:advisor>    </aop:config>8、测试代码  @Test    public void AroundAdviceTest() {        //启动工厂        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/tjcu/Spring/ApplicationContext.xml");        //获取组件 目标对象就是代理对象        StudentService studentService = (StudentService) context.getBean("studentService");        //调用方法,通过代理类调用目标类        studentService.pageShow("通过代理类调用调用目标类");    }
  • [交流分享] Spring中的常用注解
      1,     在springMVC中,controller主要是负责处理前端控制器发送过来的请求,经过业务逻辑层处理封装成一个Model,并且其返回给view经行展示。通常用于类上。如果前后端分离的项目,则使用@RestController表示返回的json格式的数据。@RestController=@Controller+@ResponseBody   2,@RequestMapping用来处理请求地址映射,可以用于类上,也可以用于方法上。用于类上的注解会将请求或者请求模式映射到一个控制器上,表示类中的所有相应都是请求的方法是以该地址作为父路径,方法级别上注解表示进一步指定处理方法的映射关系。        value:指定请求的实际地址        method:指定请求的类型,比如GET,POST,DELETE等,默认是GET类型        produces:指定返回类型,如produces="application/json,charest=utf-8"  3,@Pothvariable主要是来获取URL参数,SpringBoot支持Restfull风格的URL,例如一个GET请求携带一个参数id,我们也可以将id作为参数接收。注意:如果要想用URL中占位符的id值,直接赋值到参数id中。则要保证URL中的参数和方法中接收的参数一致,否则就要用Value属性来指定它对应关系。  4,@RequestParam也是用来获取请求参数的,与上面的区别在URL的参数风格不同。  5,@RequestBody用来接收前端传来的实体,接收参数也是对应的实体,比如前端通过Json提交传来两个参数uesrname和password。此时我们需要在后端封装一个实体接收,在参数多的情况下,使用@RequestBody接收非常方便。
  • [交流分享] Spring中的常用注解
      1,     在springMVC中,controller主要是负责处理前端控制器发送过来的请求,经过业务逻辑层处理封装成一个Model,并且其返回给view经行展示。通常用于类上。如果前后端分离的项目,则使用@RestController表示返回的json格式的数据。@RestController=@Controller+@ResponseBody   2,@RequestMapping用来处理请求地址映射,可以用于类上,也可以用于方法上。用于类上的注解会将请求或者请求模式映射到一个控制器上,表示类中的所有相应都是请求的方法是以该地址作为父路径,方法级别上注解表示进一步指定处理方法的映射关系。        value:指定请求的实际地址        method:指定请求的类型,比如GET,POST,DELETE等,默认是GET类型        produces:指定返回类型,如produces="application/json,charest=utf-8"  3,@Pothvariable主要是来获取URL参数,SpringBoot支持Restfull风格的URL,例如一个GET请求携带一个参数id,我们也可以将id作为参数接收。注意:如果要想用URL中占位符的id值,直接赋值到参数id中。则要保证URL中的参数和方法中接收的参数一致,否则就要用Value属性来指定它对应关系。  4,@RequestParam也是用来获取请求参数的,与上面的区别在URL的参数风格不同。  5,@RequestBody用来接收前端传来的实体,接收参数也是对应的实体,比如前端通过Json提交传来两个参数uesrname和password。此时我们需要在后端封装一个实体接收,在参数多的情况下,使用@RequestBody接收非常方便。
  • [技术干货] spring 框架搭建
    IOC本质控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,在以往的代码中,对象的创建由程序自己控制,控制反转后将对象的创建交由springIOC容器创建和管理,需要使用时从容器中拿到使用。控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。spring快速构建导入jar包<dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-webmvc</artifactId></dependency>新建实体类public class Hello {    private String name;     public String getName() {        return name;    }     public void setName(String name) {        this.name = name;    }     public void show() {        System.out.println("Hello," + name);    }}在resourse下创建beans.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"       xsi:schemaLocation="http://www.springframework.org/schema/beans        https://www.springframework.org/schema/beans/spring-beans.xsd">    <!--bean就是java对象 , 由Spring创建和管理-->    <bean id="hello" class="com.lyj.bean.Hello">        <property name="name" value="Spring"/>    </bean></beans>测试ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");        //getBean : 参数即为spring配置文件中bean的id .        Hello hello = (Hello) context.getBean("hello");        hello.show();通过这种方式,就可以在xml中创建和管理一个id名为hello的,类型为Hello的的实体类对象实例,在Java代码中通过方法直接获取这个实例。
  • [技术干货] spring
    IOC本质控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,在以往的代码中,对象的创建由程序自己控制,控制反转后将对象的创建交由springIOC容器创建和管理,需要使用时从容器中拿到使用。控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。spring快速构建导入jar包<dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-webmvc</artifactId></dependency>新建实体类public class Hello {    private String name;     public String getName() {        return name;    }     public void setName(String name) {        this.name = name;    }     public void show() {        System.out.println("Hello," + name);    }}在resourse下创建beans.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"       xsi:schemaLocation="http://www.springframework.org/schema/beans        https://www.springframework.org/schema/beans/spring-beans.xsd">    <!--bean就是java对象 , 由Spring创建和管理-->    <bean id="hello" class="com.lyj.bean.Hello">        <property name="name" value="Spring"/>    </bean></beans>测试ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");        //getBean : 参数即为spring配置文件中bean的id .        Hello hello = (Hello) context.getBean("hello");        hello.show();通过这种方式,就可以在xml中创建和管理一个id名为hello的,类型为Hello的的实体类对象实例,在Java代码中通过方法直接获取这个实例。
  • [技术干货] Spring Boot集成log4j日志
    一、 引入log4j依赖在创建Spring Boot工程时,我们引入了spring-boot-starter, 该依赖 默认的日志框架Logback,所以我们在引入log4j之前,需要先排除该包的依赖,再引入log4j的依赖:二、 配置log4j.properties在引入了log4j依赖之后,只需要在src/main/resources目录下加入log4j.properties配置文件,就可以开始对应用的日志进行配置使用。开发环境 控制台输出
  • [技术干货] springboot2.0整合logback
    一、开发环境开发环境• JDK 8• Spring Boot 2.0. 9 RELEASE• Maven• Windows 10• IDEA 20 20 . 3Spring Boot默认集成了Logback,可以开箱即用, 不需要再次引入 spring-boot-starter-logging 依赖 , 因为 Spring Boot启动 时加载 spring-boot-starter ,而 spring-boot-starter 包含了 spring-boot-starter-logging,所以Spring Boot就默认集成了Logback 。2、 配置信息在resource下创建logback-spring.xml文件然后在yml中配置logging.config=classpath:log/logback-spring.xmllogging.path=D:/nmyslog/nmys最后将下面的代码复制到logback-spring.xml里。<?xml version="1.0" encoding="UTF-8"?><!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 --><!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true --><!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 --><!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> < configuration scan =" true " scanPeriod =" 10 seconds "><!--<include resource="org/springframework/boot/logging/logback/base.xml"/> -->< contextName > Logback For Boss </ contextName ><!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->< property name =" log.path " value =" F:/locakback " /><!-- 定义日志文件 输入位置 -->< property name =" logDir " value =" F:/logbak " /><!-- 日志最大的历史 30天 -->< property name =" maxHistory " value =" 30 " /><!-- 控制台输出日志 -->< appender name =" STDOUT " class =" ch.qos.logback.core.ConsoleAppender ">< encoder >< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger-%msg%n </ pattern >< charset class =" java.nio.charset.Charset "> UTF-8 </ charset ></ encoder ></ appender ><!-- ERROR级别日志 -->< appender name =" ERROR "class =" ch.qos.logback.core.rolling.RollingFileAppender ">< filter class =" ch.qos.logback.classic.filter.LevelFilter ">< level > ERROR </ level >< onMatch > ACCEPT </ onMatch >< onMismatch > DENY </ onMismatch ></ filter >< rollingPolicyclass =" ch.qos.logback.core.rolling.TimeBasedRollingPolicy ">< fileNamePattern > ${logDir}\%d{yyyyMMdd}\error.log </ fileNamePattern >< maxHistory > ${maxHistory} </ maxHistory ></ rollingPolicy >< encoder >< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger -%msg%n </ pattern >< charset class =" java.nio.charset.Charset "> UTF-8 </ charset ></ encoder >< append > false </ append >< prudent > false </ prudent ></ appender ><!-- WARN级别日志 -->< appender name =" WARN "class =" ch.qos.logback.core.rolling.RollingFileAppender ">< filter class =" ch.qos.logback.classic.filter.LevelFilter ">< level > WARN </ level >< onMatch > ACCEPT </ onMatch >< onMismatch > DENY </ onMismatch ></ filter >< rollingPolicyclass =" ch.qos.logback.core.rolling.TimeBasedRollingPolicy ">< fileNamePattern > ${logDir}\%d{yyyyMMdd}\warn.log </ fileNamePattern >< maxHistory > ${maxHistory} </ maxHistory ></ rollingPolicy >< encoder >< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger-%msg%n </ pattern >< charset class =" java.nio.charset.Charset "> UTF-8 </ charset ></ encoder >< append > false </ append >< prudent > false </ prudent ></ appender ><!-- INFO级别日志 -->< appender name =" INFO "class =" ch.qos.logback.core.rolling.RollingFileAppender ">< filter class =" ch.qos.logback.classic.filter.LevelFilter ">< level > INFO </ level >< onMatch > ACCEPT </ onMatch >< onMismatch > DENY </ onMismatch ></ filter >< rollingPolicyclass =" ch.qos.logback.core.rolling.TimeBasedRollingPolicy ">< fileNamePattern > ${logDir}\%d{yyyyMMdd}\info.log </ fileNamePattern >< maxHistory > ${maxHistory} </ maxHistory ></ rollingPolicy >< encoder >< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger-%msg%n </ pattern >< charset class =" java.nio.charset.Charset "> UTF-8 </ charset ></ encoder >< append > false </ append >< prudent > false </ prudent ></ appender ><!-- DEBUG级别日志 -->< appender name =" DEBUG "class =" ch.qos.logback.core.rolling.RollingFileAppender ">< filter class =" ch.qos.logback.classic.filter.LevelFilter ">< level > DEBUG </ level >< onMatch > ACCEPT </ onMatch >< onMismatch > DENY </ onMismatch ></ filter >< rollingPolicyclass =" ch.qos.logback.core.rolling.TimeBasedRollingPolicy ">< fileNamePattern > ${logDir}\%d{yyyyMMdd}\debug.log </ fileNamePattern >< maxHistory > ${maxHistory} </ maxHistory ></ rollingPolicy >< encoder >< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger-%msg%n </ pattern >< charset class =" java.nio.charset.Charset "> UTF-8 </ charset ></ encoder >< append > false </ append >< prudent > false </ prudent ></ appender ><!--文件日志, 按照每天生成日志文件 -->< appender name =" FILE " class =" ch.qos.logback.core.rolling.RollingFileAppender ">< rollingPolicy class =" ch.qos.logback.core.rolling.TimeBasedRollingPolicy "><!--日志文件输出的文件名-->< FileNamePattern > ${logDir}/%d{yyyyMMdd}/boss.%d{yyyy-MM-dd}.log </ FileNamePattern ><!--日志文件保留天数-->< MaxHistory > 30 </ MaxHistory ></ rollingPolicy >< encoder class =" ch.qos.logback.classic.encoder.PatternLayoutEncoder "><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->< pattern > %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n </ pattern ></ encoder ><!--日志文件最大的大小-->< triggeringPolicy class =" ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy ">< MaxFileSize > 10MB </ MaxFileSize ></ triggeringPolicy ></ appender ><!-- 异步输出 -->< appender name =" dayLogAsyncAppender " class =" ch.qos.logback.classic.AsyncAppender ">< includeCallerData > true </ includeCallerData ><!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->< discardingThreshold > 0 </ discardingThreshold ><!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->< queueSize > 512 </ queueSize >< appender-ref ref =" FILE "/></ appender ><!--专为 spring 定制-->< logger name =" org.springframework " level =" info "/><!-- show parameters for hibernate sql 专为 Hibernate 定制 -->< logger name =" org.hibernate.type.descriptor.sql.BasicBinder " level =" TRACE " />< logger name =" org.hibernate.type.descriptor.sql.BasicExtractor " level =" DEBUG " />< logger name =" org.hibernate.SQL " level =" DEBUG " />< logger name =" org.hibernate.engine.QueryParameters " level =" DEBUG " />< logger name =" org.hibernate.engine.query.HQLQueryPlan " level =" DEBUG " /><!--myibatis log configure--><!--<logger name="com.apache.ibatis" level="TRACE"/><logger name="java.sql.Connection" level="DEBUG"/><logger name="java.sql.Statement" level="DEBUG"/><logger name="java.sql.PreparedStatement" level="DEBUG"/>--><!-- root级别 DEBUG -->< root level =" INFO "><!-- 控制台输出 -->< appender-ref ref =" STDOUT " /><!-- 文件输出 -->< appender-ref ref =" ERROR " />< appender-ref ref =" INFO " />< appender-ref ref =" WARN " />< appender-ref ref =" DEBUG " /></ root ></ configuration >大工告成!
  • [技术干货] IOC 容器的初始化过程
      最近复习了一遍Spring IOC容器的初始化过程,结合书籍《Spring源码深度解析》总结了一下,IOC容器的初始化过程,大概分为以下三点:1、定位资源:  定位相关的配置文件,扫描相关注解2、加载资源:  将配置信息加载到内存中3、注册:  根据载入的配置信息,初始化对象,并将其装载至容器中POM文件<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springTest</artifactId> <version>1.0-SNAPSHOT</version><dependencies> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency></dependencies></project>测试代码public class ServiceB { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); Object serviceA = context.getBean("serviceA"); System.out.println(serviceA); }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:context="http://www.springframework.org/schema/context" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p" 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/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <context:component-scan base-package="com.donkeys.spring"/> <bean id="serviceA" class="com.donkeys.spring.service.ServiceA"></bean></beans>ClassPathXmlApplicationContext构造方法 public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); //根据传入的配置文件名称,调用父类的setConfigLocations方法,解析配置文件路径, setConfigLocations(configLocations); //refresh = true //refresh() 方法会重启整个容器 if (refresh) { refresh(); } }  构造方法中一共做了2件事,首先是设置配置文件的路径,然后对整个容器进行刷新。  这里我们重点关注refresh()方法;进入refresh()方法AbstractApplicationContext类的refresh()方法@Overridepublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. //为刷新前做准备 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. //获取IOC容器,这里就是处理资源定位以配置文件加载/注册的方法 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } }}  这里主要关注**obtainFreshBeanFactory()**方法/** * Tell the subclass to refresh the internal bean factory. * @return the fresh BeanFactory instance * @see #refreshBeanFactory() * @see #getBeanFactory() */protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //刷新IOC容器 //这里使用了委派设计模式,父类定义了抽象的refreshBeanFactory方法,具体调用实现调用子类的refreshBeanFactory方法 refreshBeanFactory(); //获取一个新的容器 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory;}  **obtainFreshBeanFactory()**方法总共干了2件事,  重置容器,refreshBeanFactory()方法中会设置相关标志,清除旧的容器,同时为Spring上下文生成一个新的容器,获取一个新的容器AbstractRefreshableApplicationContext的refreshBeanFactory()方法  下面我们进入**refreshBeanFactory()**方法/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. * 该方法会将之前的bean工厂全部关闭,并初始化一个全新的bean 工厂类 用于Spring 上下文的生命周期 * bean工厂就是IOC容器 */@Overrideprotected final void refreshBeanFactory() throws BeansException { //判断是否之前也有容器 //如果有就销毁掉 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建一个新的工厂 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //读取Bean对象的定义 //这里也是使用的委派设计模式 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); }}  这里创建了新的容器工厂,同时将新的工厂传入了loadBeanDefinitions()方法中,下面来看一下在loadBeanDefinitions方法中具体做了什么操作。AbstractXmlApplicationContext的 loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法  在AbstractRefreshableApplicationContext类的refreshBeanFactory方法中,调用了loadBeanDefinitions方法,但是这个方法它的一个抽象方法,具体实现应由子类去实现,我们在程序启动时,使用的ClassPathXmlApplicationContext类。根据文章开头的类图可以知道,这里会调用子类AbstractXmlApplicationContext的loadBeanDefinitions方法去完成本次加载@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //使用默认的beanFactory去创建 XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. //设置资源的加载环境 beanDefinitionReader.setEnvironment(this.getEnvironment()); //设置资源读取器 beanDefinitionReader.setResourceLoader(this); //设置实体解析器 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. //初始化 bean对象定义读取器 initBeanDefinitionReader(beanDefinitionReader); //使用初始化完成的读取器,调用loadBeanDefinitions方法 loadBeanDefinitions(beanDefinitionReader);}  总的来说,这里只干了一件事,那就是初始化配置//设置资源读取器beanDefinitionReader.setResourceLoader(this);//初始化 bean对象定义读取器initBeanDefinitionReader(beanDefinitionReader);  设置资源读取器,这里设置的资源读取器就是当前这个对象本身  通过类图我们可以发现我们这个类的顶级父类ApplicationContext,继承自DefaultResourceLoader这个类,该类实现了ResourceLoader接口,说明这个类的实例化对象本身是具有资源读取器的功能的  初始化bean对象定义读取器,这里设置xml文件的校验方式  下面我们继续看**loadBeanDefinitions(XmlBeanDefinitionReader reader)**方法protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //从子类对象中获取到资源定位 Resource[] configResources = getConfigResources(); if (configResources != null) { //XmlBeanDefinitionReader 读取器调用其父类的 reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); }}  先看这一行//这行代码的具体实现是由子类完成的,通过类图可以知道AbstractXmlApplicationContext的子类为ClassPathXmlApplicationContext//这里主要将我们最开始在构造方法中设置好的配置文件进行返回Resource[] configResources = getConfigResources();  再看这一行//如果返回的配置文件不为空,就将返回的已经封装好的资源文件进行读取, if (configResources != null) { //XmlBeanDefinitionReader 读取器调用其父类的 reader.loadBeanDefinitions(configResources); }  至此。资源文件定位过程已经加载完成。后续就是读取和注册。整个IOC容器加载过程中最重要的是读取过程,我们可以从刚刚的定位过程来看,虽然叫定位过程,但是其实就是一个配置文件读取器的初始化过程,这个过程会设置相关的解析策略以及校验策略。最终读取器生成后,就可以将我们早早设置好的配置文件载入然后进行读取。
总条数:78 到第
上滑加载中