Spring 学习
Spring IOC 的介绍
- 问题
在使用MVC的结构体系来完成后台功能代码的声明时,在一定程度上降低了代码的冗余,但是层与层之间的耦合性过高,造成代码升级维护特别麻烦,比如,某天业务层某个类文件需要替换为新的类文件,那么,控制层所有调用该业务类的代码需要全部修改为调用新的业务类。 - 解决
将层与层对象之间的关系进行解耦,由直接变为间接,简而言之,就是Spring IOC 用来解决层与层之间耦合读过高的问题。 - 原理
化直接为间接。比如A类要调用B类中的方法,变为A类调用C类,从C类中获取B对象,这个过程Spring帮我们做。
Spring IOC 创建对象的三种方式
1、获取Spring容器对象
//创建容器对象--解析XML
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationcontext.xml");
2. 通过构造器方式
<!--
无参构造器
特点:Spring容器默认使用无参构造方式创建对象
使用:在配置文件中直接使用bean标签配置即可,无需过多声明
-->
<bean id="stu" class="Student"></bean>
<!--有参数的构造器
特点:Spring容器对根据配置调用的有参构造器创建一个带有初始化数据的对象
使用:constructor-arg:使用bean的字标签来声明调用的构造器的形参的个数
一个字标签表示一个参数
属性:index:参数的下标
type:参数的类型,全限定路径
name:参数的形参名
value:参数要给的值
-->
<bean id="stu2" class="com.bjsxt.pojo.Student">
<constructor-arg index="0" type="java.lang.Integer" name="sid" value="1"></constructor-arg>
<constructor-arg index="1" type="java.lang.String" name="sname" value="张三"></constructor-arg>
</bean>
3、通过属性注入的方式(get/set)
<!--创建student的bean对象-->
<!--
属性注入方式
特点:相当于创建一个空对象然后使用set方法赋值
使用:
property:在bean标签下使用子标签property,表示调用set方法给某个属性赋值
属性:name:要赋值的属性名
value:值
-->
<bean id="stu3" class="Student">
<property name="sid" value="2"></property>
<property name="sname" value="小王"></property>
</bean>
4、工厂模式
<!--动态工厂-->
<bean id="factory" class="StudentFactory"></bean>
<!--生产Student对象-->
<bean id="stu4" factory-bean="factory" factory-method="newIntance"></bean>
<!--静态工厂-->
<!--可以理解为静态方法直接用类名调用-->
<bean id="stu5" class="StudentFactory2" factory-method="newIntance"></bean>
Spring IOC的依赖注入DI
- DI依赖的使用流程
1、将依赖责任链上的所有的对象都配置为bean
2、根据依赖关系完成对象之间的组装配置 - 实现方式
1、通过构造器方式
2、 通过set方法方式
<!--配置学生bean对象-->
<bean id="stu" class="Student">
<!--构造器方式-->
<!--<constructor-arg index="0" name="teacher" type="Teacher" ref="tea" ></constructor-arg>-->
<!--set方式-->
<property name="teacher" ref="tea"></property>
<property name="sname" value="张三"></property>
<property name="sid" value="1"></property>
</bean>
<bean id="tea" class="com.bjsxt.pojo.Teacher">
<property name="tid" value="2"></property>
<property name="tname" value="刘老师"></property>
</bean>
Spring IOC 创建对象的单例多例方式
-
问题?
由于Spring容器对象底层使用的是map集合存储的bean对象,对map集合按照同一个键名获取数据,获取的是同一个,也就说按照同一个键名从Spring容器中获取的都是同一个对象,那么如果我们希望相同的键名获取的对象每次都不一样,怎么实现? -
解决
不要在Spring容器对象创建的时候,就完成对象的初始化创建,而是变为,从Spring容器中获取的时候才创建,每次获取都重新创建。 -
代码展示
<!--
单例模式:默认模式,在bean标签上使用scope属性,默认值为singleton
多例模式:在bean标签上使用scope属性设置,值为prototype
-->
<bean id="stu" class="Student" scope="singleton"></bean>
<bean id="tea" class="Teacher" scope="prototype"></bean>
SpringIOC 的自动注入
1、根据bean的ID和属性名一致的规则
2、根据bean的类型和属性的类型一致的规则
3、根据构造器形参的类型和bean的类型一致的规则
Spring AOP 介绍学习
- 引入
当我们需要对某一个功能方法,进行升级时,这个代码时小王写的,他已经辞职,并且现在我需要去升级这段功能,给我造成的很大的烦恼;一是、代码很难看懂,二是、新增的代码可能与之前会有冲突,如变量名、耦合性问题等。那么有没有什么方法,能不能不改变原有的代码基础上,来使我们完成代码的升级呢? - 解决
1、使用接口的方式实现
对象A和对象B共同实现C接口,在新的对象B中重写test方法,然后调用A类的方法+要升级代码。此方法耦合过高,多次升级,就要改很多代码。
2、升级
SpringAOP的专业概念
1、真实对象:要进行功能扩展的对象
2、代理对象:完成功能扩展的对象
3、切点:要进行功能扩展的方法 execution(* com.ssm.service.impl.*.*(..))
SpringAOP的SchemaBase方式实现
1、前置通知类,实现MethodBeforeAdvice接口, 重写before方法,在before方法中声明扩展前的逻辑代码。
/**
*
* @param method 切点方法对象
* @param objects 参数
* @param o 真实对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("MyBefore.before:我是扩展前");
}
2、后置通知类,实现AfterReturningAdvice接 口,重写after方法,并在after方法中声明扩展后的逻 辑代码。
/**
*
* @param o 代理对象
* @param method 切点方法对象
* @param objects 参数
* @param o1 真实对象
* @throws Throwable
*/
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("MyAfter.afterReturning:我是扩展后");
}
3、异常类,实现异常接口ThrowsAdvice,重写afterThrowing方法。
4、环绕通知,实现MethodInterceptor接口,重写invoke方法。
<!--配置Student的bean-->
<bean id="stu" class="com.longlong.pojo.Student"></bean>
<!--配置扩展bean-->
<!--配置前置通知-->
<bean id="before" class="com.longlong.advice.MyBefore"></bean>
<!--配置后置通知-->
<bean id="after" class="com.longlong.advice.MyAfter"></bean>
<!--配置环绕bean-->
<bean id="round" class="com.longlong.advice.Myround"></bean>
<!--配置异常信息-->
<bean id="thorw" class="com.longlong.advice.MyThrow"></bean>
<!--配置组装规则-->
<aop:config>
<!--声明切点-->
<aop:pointcut id="mp" expression="execution(* com.longlong.pojo.Student.testStudent(String,int))"/>
<!--前置通知-->
<aop:advisor advice-ref="before" pointcut-ref="mp"></aop:advisor>
<!--后置通知-->
<aop:advisor advice-ref="after" pointcut-ref="mp"></aop:advisor>
<!--环绕通知-->
<aop:advisor advice-ref="round" pointcut-ref="mp"></aop:advisor>
<!--异常通知-->
<aop:advisor advice-ref="thorw" pointcut-ref="mp"></aop:advisor>
</aop:config>
SpringAOP的Aspectj方式实现
Java类MyAdvice
//前置通知
public void before(){
System.out.println("我是前置通知");
}
//后置通知
public void after(){
System.out.println("我是后置通知");
}
//环绕通知
public void myRound(ProceedingJoinPoint pp) throws Throwable {
//环绕前
System.out.println("环绕前");
//放行
Object proceed = pp.proceed();
//环绕后
System.out.println("环绕后");
}
//异常通知
public void myThrow(Exception e){
System.out.println("异常通知"+e.getMessage());
}
<!--配置学生的bean-->
<bean id="stu" class="com.longlong.pojo.Student"></bean>
<!--配置通知bean-->
<bean id="advice" class="com.longlong.advice.MyAdvice"></bean>
<!--配置组装规则-->
<aop:config>
<aop:aspect ref="advice"><!--声明通知bean的ID-->
<!--声明切点-->
<aop:pointcut id="mp" expression="execution(* com.longlong.pojo.Student.testStudent())"/>
<!--声明前置扩展方法-->
<aop:before method="before" pointcut-ref="mp"></aop:before>
<!--声明后置-->
<aop:after method="after" pointcut-ref="mp"></aop:after>
<!--配置环绕通知-->
<aop:around method="myRound" pointcut-ref="mp"></aop:around>
<!--异常通知-->
<aop:after-throwing method="myThrow" pointcut-ref="mp" throwing="e"></aop:after-throwing>
</aop:aspect>
</aop:config>
Acspectj和SchemaBased的异同
1、相同点
在不修改源码的情况下都能实现功能的扩展。
2、不同点
(1)SchemaBased方式基于接口来区别前置和后置和环绕和异常通知的,而AspectJ方式是在配置文件中使用标签来区分。
(2)AspectJ方式在配置中的配置方式发现其切点的声明以及对应的通知组装中,切点只在Aop:aspect标签下有效。而SchemaBased方式声明的切点在全局有效.SchemaBased的切点的通用性比AspectJ方式要好。
(3)参数方面,SchemaBased相对于有无参数来说,更加方便使用。
Spring AOP之代理设计模式
1、静态代理
由程序员自己写。
2、动态代理
(1)jdk动态代理--基于接口 (默认)
public interface MyInterface {
//声明功能方法
void testStudent();
}
public class Myjdk implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("扩展前");
Student student = new Student();
student.testStudent();
System.out.println("扩展后");
return null;
}
}
public class TestProxyJDK {
public static void main(String[] args) {
//使用jdk动态代理获取Student对象的代理对象
MyInterface myInterface = (MyInterface) Proxy.newProxyInstance(
TestProxyJDK.class.getClassLoader(), //类加载器,加载动态生成的代理对象
new Class[]{MyInterface.class},//动态生成的代理对象要实现的接口
new Myjdk());//动态生成的代理对象自动调用扩展代码 InvocationHandler的实例化对象
//调用
myInterface.testStudent();
}
}
(2)cglib动态代理--基于继承
public class Student {
public void testStudent(){
System.out.println("Student.testStudent:我是学生");
}
}
public class Mycglib implements MethodInterceptor {
/**
*
* @param o 代理对象
* @param method 真实方法对象
* @param objects 参数
* @param methodProxy 代理方法对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib扩展前");
//调用真实方法
methodProxy.invokeSuper(o,objects);
System.out.println("cglib扩展后");
return null;
}
}
public class TestSpringCglib {
public static void main(String[] args) {
//使用cglib获取动态生成student代理对象
Enhancer en = new Enhancer();//创建cglib对象
en.setSuperclass(Student.class);//动态生成的代理对象需要继承的真实对象
en.setCallback(new Mycglib());//动态生成的代理方法要调用的代码
Student student = (Student) en.create();//生成代理对象
student.testStudent();
}
}
<!-- 开启cglib动态代理-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
Spring TX事务
1、XML配置
<!--SpringTX的事务管理-->
<!--配置事务管理bean-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务管理方法-->
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="sel*" read-only="true"/>
<tx:method name="ins*"/>
<tx:method name="up*"/>
<tx:method name="del*"/>
</tx:attributes>
</tx:advice>
<!--配置事务管理的切面-->
<aop:config>
<!--声明业务方法的切点-->
<aop:pointcut id="mp"
expression="execution(* com.longlong.service.impl.*.*(..))"/>
<!--增加事务通知-->
<aop:advisor advice-ref="advice" pointcut-ref="mp"></aop:advisor>
</aop:config>
Spring 常用注解
@Component:使用在普通java类上。
@Service:使用在业务层类上。
@Controller:使用在控制层类上,SpringMVC。
@Resource:jdk官方的注解,不是Spring的注解,会先按照byName的方式注入。
@Autowired:Spring官方提供的,默认使用byType。
@Value:用来替换配置文件中的属性注入的。