未完...待更新
一、spring概述
spring是JavaEE/SE的轻量级组件,总的来说它解决了IBM提供的EJB重量级组件的问题。spring提供了表现层、持久层、切面编程(AOP)、控制反转(Ioc)、spring的测试这五个大板块。利用spring框架我们可以很好的解决JavaEE开发问题。
二、控制反转(Ioc)
1、引入
通过原始的Java的JDBC数据库编程中注册驱动以驱动对象注册时,会违反编程的一个原则:编译不依赖,运行依赖。
解决方案一:以反射创建驱动对象,但仍然有问题,就是驱动类名硬编码到程序中了。
解决方案二:还是以反射创建对象,但要把被创建的对象全类名配置到配置文件里,以工厂模式来解决,但这种方式还是有不完美的,就是每次以工厂的方法获取对象时,对于同一个类,获得的对象都是不同的,即是线程安全的对象也会创建成多例。
解决方案三:以工厂模式创建,同时在工厂里加入一个容器(Map)来存放当加载完配置文件后就创建成功的对象。之后程序员就通过工厂提供的方法,以bean对象在容器里的Key来取对应的对象。
通过上面的问题背景和解决方案的我们已经说完了SpringIoc的核心思想,即springIOC会提供容器(Map)来帮助我们创建和管理这些bean对象,同时也允许我们引用这些bean(即DI依赖注入),而我们要做的就是在spring配置文件里配置好我们创建的对象的“全类名”和“在容器里的key”。
2、springIoc入门
2.1、springIoc的XML配置方式
<bean id="" class=""/>
复制代码
其中id表示对象存放到容器里的key,class就是对象所属的类全名
*创建对象的两种方式:
1、beanFactory:以这个容器来获取spring容器时,它会延迟加载即用到对象时才创建该对象。
2、ApplicationContext:创建对象时会加载完配置文件就创建
*创建对象的三种方式
1、无参构造函数
<bean id="" class=""/>
复制代码
2、静态工厂的方法
*注意静态工厂就是它里面的方法是静态的,所以在配置bean时要加上factory-method=""属性来引用静态工厂的方法
复制代码
<bean id="" factory-method="staticFactoryCreateBean" class=""/>
复制代码
3、实例工厂的方法
*实例工厂的方法不是静态的,所以要先配置实例工厂的bean再在要引用实例工厂方法的bean上加上factory-class、factory-method属性
复制代码
<bean id="instanceFactory" class="">
<bean id="" factory-class=instanceFactory" factory-method=""/>
复制代码
*springIoc入门小程序
配置好配置文件后,只要获取管理这些bean的容器即可调用这些容器里bean的方法。而获取spring容器和获取容器里bean的代码如下:
ApplicationContext application = new ClassPactXmlApplicationContext("bean.xml");
UserService userService=application.getBean("userSerivice");
userService.xxx//调用userService对象的方法
复制代码
2.2、springIoc的注解配置 *创建对象的注解
@Component(value="")
@Service
@Repository
复制代码
其中value不写时是类的短名和首字母小写,这三个注解效果都差不多,都可以创建对象,可以互换,但还是按使用场景使用。
2.3、springIoc的DI依赖注入
2.3.1、XML的注入方式:
*有参构造函数:
<constructor-arg type="" name="" value/ref=""/>
复制代码
type:注入数据的类型
name:注入数据的名字
ref:引用其他bean类型数据
value:简单数据类型和string类型
*调用set方法
<property name="" value/ref=""/>
复制代码
type:注入数据类型
name:注入数据名字
ref:引用其他bean类型
value:简单数据类型和string类型
*复杂数据类型的注入(集合)
list类型集合:
<property name="myList">
<array>
<value>AA</value>
</array>
</property>
复制代码
其中标签array可以替换为set或者list,用法都是一样的
Map类型集合:
<property name="myMap">
<entry key="" value=""/>
</property>
<property name="myMap">
<props>
<prop key="">AA</prop>
<props>
复制代码
以上的两种写法都可以替换
2.4、springIoc的注解注入
注解注入的常见注解
@Autowired
@Qualifier(value="")
@Resource(name="")
@Value(value="")
复制代码
其中@Autowired是按类型注入,如果有多个相同的类型对象,那么才会按名字匹配;而@Qualifier是基于类型的按名字匹配,对于字段的注入时不能单独使用,但和形参注入时可以单独使用;@Resource是按名字注入;@Value是对简单数据类型的注入,当然也可以从配置文件里读取信息。
配置类相关的注解
@Bean
复制代码
用于把方法的返回值保存到容器里,默认以方法名作为容器中该对象的key
@Configuaration
@ComponentScan(value={})
@Import(value={})
@PropertySource(value="classpath:xxx.properties")
复制代码
@Configuration用于告知spring这个类是配置类,配置类等效于spring的配置文件
@ComponenetScan(value={})注解版的扫描包
@Import(value={})导入其他配置类,但不是把其他配置类加入容器中
@PropertySource()只能加载classpath下的.properties类型的文件
2.5、springIoc的纯注解版
这时就要用配置类来替换spring的配置文件,并把以前在配置文件里配置的信息全部换成注解和在配置类里完成。
3、spring的切面编程(AOP)
3.1、概述
切面编程(AOP),其核心思想就是在不改变源码的基础上能动态的为原对象增加功能;而实现这一功能的方式就是动态代理。
3.2、spring中动态代理方式
1、基于接口的动态代理
要求:被代理对象必须至少实现一个接口
提供方:JDK官方
涉及的类:Proxy
涉及的方法:newproxyInstance(ClassLoader,Class[],InvocationHandler)
参数解释:
ClassLoader:类加载器,即指定加载动态代理类的加载器,一般默认就是用被代理对象的加载器
Class[] interfaces:即指定被代理对象实现的接口,一般用反射来获取
InvocationHandler:一个接口,在这个接口里一般以匿名内部类来实现,而这个接口里的方法则是我们编写如何增强被代理对象的实现。
2、基于子类的动态代理
要求:被代理类不能被final修饰
提供方:CGLIB
涉及类:Enhancer
涉及方法:create(ClassLoader,Callback)
参数解释:
classLoader:同上
Callback:也是一个接口,我们使用子类,MethodInterceptor,而它子类里的方法,interceptro(proxy,method,args,proxymethod),同样也是我们编写具体怎么增强的方法
3.3、AOP的原理
其实为什么不用改源码就可以增强方法?
其实,在生成的动态代理对象里,它具有和被代理对象一样的行为,当程序员在调用代理对象里和被代理对象的相同方法时,其实代理对象里的方法去调用了intercepto和invoke,而interceptor方法里又去调用真正的目标对象里的这个方法,而我们可以在interceptor方法中对于在调用目标对象方法前后添加系统代码实现增强目标代码的功效。
3.4、AOP的应用
数据源(dbcp)中的连接对象的close()方法,这个close方法并没有真正的关闭数据连接对象,而是把连接对象放到了数据源的缓存里。
再比如web开发时业务层的方法要实现事务的支持,我们也可以用动态代理的实现,返回具体事务支持的service对象
3.5、AOP的XML配置
*配置具体增强的类(通知)
*配置aop
*配置被增强的方法(切入点)和增强方法具体的增强细节
<bean id="txAdvice" class="xxxx"/>
<aop:config>
<aop:aspect id="logger" ref="txAdvice>
<aop:before pointcut="execution(* com..*.*(..))" method="">
<!--</aop:aspect>-->
</aop:config>
复制代码
注意:切入点表达式配置在aspect里时只能在这个切面里使用该切入点表达式,但如果配置在aspect外面,那么就可以在多个aspect中使用
<aop:config>
<aop:pointcut id="pt" expression="execution(* com..*.*(..)")/>
<aop:aspect id="" ref="txAdvice">
<aop:before emthod="" pointcut-ref="pt">
</aop:aspect>
</aop:config>
复制代码
切入点表达式配置:
访问修饰 返回值 包名.包名.....类名.方法名(参数列表)
访问修饰符可以不写
放回值可以用*表示任意类型
包名可以用.的方式来表示有多少个包或者com..来表示com包及其子包
类名可以用“*”替代任意类
参数如果是基本类型可以直接写如果是引用类型那么就要包名.类名;也可以*表示任意类型但必 须要有参数;还可以是..表示任意类型或者没有参数
3.6、AOP的注解和XML配置
1、首先要加配置类@Configuration
2、其次扫描包@CompnentScan(value={})
3、配置切面@Aspect
4、在xml配置文件里开启切面注解配置
<aop:aspectj-autoproxy/>
复制代码
5、对于配置类里面的方法上可以使用
@Before
@AfterReturning
@AfterThrowing
@After
@Around
复制代码
这几个注解来表达这些方法分别属于什么类型的通知
6、在配置类里加上一个空参的方法在这个方法上配置切入点表达式
@PointCut("execution(* com..*.*(..)"))
public void pointcut(){}
复制代码
7、之后再各通知方法上引入这个切入点即可
@Before("pointcut()")
public void log(){
}
复制代码
3.8、AOP的纯注解
其实就是把在配置文件里加入的开启aop注解配置的开关标签换成在配置类上加入一个注解来开启aop的注解配置 @EnableAspectJAutoProxy
3.9、通知里比较特殊的通知环绕通知
在以配置类配置环绕通知时要注意,环绕配置里要用明确的目标方法调用,因此spring为我们提供了一个参数接口ProceedingJoinPoint这个接口在运行时有spring来创建实现类,它的proceed()方法等效于interceptor方法,所以我们可以在环绕通知里手动配置各种通知类型的具体方法
Public Object aroundPrint(ProceedJointPoint pjp){
Object retVal;
Try{
//前置通知
retVal = Pjp.proceed();
//后置通知
}catch(Throwable e){
//异常通知
}finally{
//最终通知
}
复制代码
4、springJdbcTemplate
关键点:spring对于持久层的操作推出的是JdbcTemplate,而对于JdbcTemplate在Dao层的使用时最好Dao的实现类去实现一个JdbcDaoSupport的接口,实现后它可以方便在spring配置文件里在对Dao层配置时简化对JdbcTemplate的配置(即只配置数据源就可以配置好Dao中配置好JdbcTemplate了)
实战: 获取JdbcTemplate对象
JdbcTemplate jd = new JdbcTemplate(dataSource);
复制代码
直接new就可以了。但要操作数据库时注意update是对更新、插入、去除语句操作的,query是对查询操作的,而其中对于查询结果映射为对象时采用的是RowMapper这个接口。通常我们使用的是BeanPropertyRowMapper这个接口,它可以返回多行时的对象、单行时的对象。而对于单个值的映射,采用的是queryForObject()的方法,其中这个方法里可以指定返回的数字的类型。
还有要注意的一点是springjdbcTemplate提供了一个内置的数据源即DataSourceManager,我们也可以使用它。
5、spring的事务管理
引入:spring中共提供了三个与事务有关的接口:PlatformTransactionManager(事务管理器的接口)、TransactionDefinition(事务属性接口)、TransactionStatus(事务运行状态接口)
PlatformTransactionManager:其实现类如果持久层采用的是mybatis那么就用DataSourceTransactionManager,如果是Hibernate那么就采用其他的事务管理器的配置,总之一句话就是spring会根据你持久层采用的是什么框架来分别提供了对应的事务管理器的实现类。即事务管理器定义了如何提交、回滚等方法。
TransactionDefinition:
getName()获取事务名称
getIsolationLevel():获取事务隔离状态
getPropagationBehavior():获取事务传播行为
getTimeout():获取超时时间,默认为-1,即不超时
isReadyOnly():事务是否是只读事务,一般查询是只读事务
复制代码
对于事务的这些属性,通常我们可以在配置文件里配置通知的时候配置
TransactionStatus:
flush()
hasSavePoint()
isCompleted()
isNewTransaction()
isRollback()
复制代码
这个接口里的方法是返回事务运行时的状态。
1、XML的事务配置
配置事务管理器
<bean id="transactionManager" class="xxx"/>
配置事务通知
<tx:advice id="txAdvice" transaction-Manager="transactionManager">
<tx:attribute>
<tx:method propagtion="REQUEIRED" isReadOnly="true" method="*">
</tx:attribute>
</tx:advice>
配置aop
<aop:config>
<aop:pointcut expression="execution(* com..*.*(..))" id="pt"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
复制代码
2、XML和注解版
配置事务管理器
<bean id="transactionManager" class="xxx"/>
开启事务注解配置的开关
<tx:annotation-driven transaction-manager="transactionManager"/>
在业务层中在所需事务的方法上@Transaction注解,同时也可以在这个注解上配置事务的属性
复制代码
3、纯注解版
这里就仅提供思路了:
配置类
开启事务注解配置@EnableTransactionManagement