一 简述Spring核心
控制反转:IOC—>创建对象的过程
依赖注入: DI---->初始化对象属性
面向切面:AOP--->不改变原码的前提,在其方法前后插入业务逻辑代码
二 依赖注入
2.1 set方法注入
简述: B类对象持有A类对象,spring通过无参构造反射创建B对象,并将A对象当作参数传入set方法赋值给A对象变量;
set方法注入用的较多,它可以执行无参构造方法创建对象,比较灵活!
spring.xml
<bean id="a" class="cn.xgl.set.A"></bean>
<bean id="b" class="cn.xgl.set.B">
<property name="a" ref="a"/>
</bean>
java
public class A {
}
/***********************************************/
public class B {
private A a;
public B() {
System.out.println("执行了B无参构造...");
}
public A getA() {
return A;
}
//配置bean标签,spring通过参数传递对象并赋值--->set依赖注入
public void setA(A a) {
System.out.println("执行了set方法....");
this.a = a;
}
}
2.2 构造方法注入
2.2.1 构造方法参数名称注入 name
<bean id="a" class="cn.xgl.construct.A"></bean>
<bean id="b" class="cn.xgl.construct.B">
<constructor-arg name="a" ref="a"/>
<constructor-arg name="name" value="小猪猪"/>
<constructor-arg name="phone" value="12373468376"/>
</bean>
2.2.2 构造方法参数顺序注入 index
<<bean id="a" class="cn.xgl.construct.A"></bean>
<bean id="b" class="cn.xgl.construct.B">
<constructor-arg index="0" ref="a"/>
<constructor-arg index="1" value="小猪猪"/>
<constructor-arg index="2" value="12373468376"/>
</bean>
2.2.3 构造方法参数类型注入 type 相同类型需要注意顺序
<!--如果类型一样,按constructor-arg顺序匹配-->
<bean id="a" class="cn.xgl.construct.A"></bean>
<bean id="b" class="cn.xgl.construct.B">
<constructor-arg type="cn.xgl.construct.A" ref="a"/>
<constructor-arg type="java.lang.String" value="小猪猪"/>
<constructor-arg type="java.lang.String" value="******"/>
</bean>
3.内部bean与外部bean
如果有一个是我们自己的对象,可以使用 —>内部bean(局部)与外部bean(全局)
<!--外部部bean-->
<bean id="a" class="cn.xgl.construct.A"></bean>
<bean id="b" class="cn.xgl.construct.B">
<property name="a" ref="a"/>
</bean>-->
<!--内部bean-->
<bean id="c" class="cn.xgl.construct.C">
<property name="D">
<bean class="cn.xgl.construct.D"></bean>
</property>
</bean>-->
三 Set注入练习
public class Employee {
// 简单属性
private Long id;
private String name;
private Boolean sex;
private BigDecimal salary;
// 对象属性
private String[] arrays;
private List<String> list;
private Set<String> set;
private Map<String, Object> map;
//其他bean
private Set<OtherBean> otherBeanSet;
private List<OtherBean> otherBeanList;
private Properties props1;
private Properties props2;
//省略构造/get/set/tostring方法....
//ps:
//集合或数组使用它打印: System.out.println("--->"+ Arrays.asList(arrays));
}
<bean id="otherBean01" class="cn.xgl.practice.OtherBean"/>
<bean id="otherBean02" class="cn.xgl.practice.OtherBean"/>
<bean id="otherBean03" class="cn.xgl.practice.OtherBean"/>
<bean class="cn.xgl.practice.Employee">
<!--简单类型之属性注入 property标签_____Spring自动类型转换-->
<property name="id" value="1"/>
<property name="name" value="小猪猪"/>
<property name="sex" value="true"/>
<property name="salary" value="10000"/>
<!--简单类型的数组类型注入 方式一使用array标签-->
<!--<property name="arrays">
<array>
<value>1</value>
<value>2</value>
<value>3</value>
</array>
</property>-->
<!--简单类型的数组类型注入 方式二,号分隔-->
<property name="arrays" value="1,2,3,4"/>
<!--简单类型的List集合类型注入-->
<!--<property name="list">
<list>
<value>技术部</value>
<value>iT部</value>
<value>人事部</value>
</list>
</property>-->
<property name="list" value="人事部,技术部"/>
<!--简单类型的Set类型注入 方式二,号分隔-->
<!--<property name="set" value="人事部,技术部"/>-->
<property name="set">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<!--简单类型___map属性的注入-->
<property name="map">
<map>
<entry key="map1" value="put1"/>
<entry key="map2" value="put2"/>
<entry key="map3" value="put3"/>
<entry key="map3" value-ref="otherBean01"/>
</map>
</property>
<!--bean类型的数组或集合注入 -->
<property name="otherBeanList">
<list>
<!--外部bean-->
<!--<ref bean="otherBean01"/>
<ref bean="otherBean02"/>
<ref bean="otherBean03"/>-->
<!--内部bean 不去id也不会报错,作用域问题-->
<bean id="otherBean01" class="cn.xgl.practice.OtherBean"/>
<bean id="otherBean02" class="cn.xgl.practice.OtherBean"/>
<bean id="otherBean03" class="cn.xgl.practice.OtherBean"/>
</list>
</property>
<property name="otherBeanSet">
<list>
<!--外部bean-->
<!--<ref bean="otherBean01"/>
<ref bean="otherBean02"/>
<ref bean="otherBean03"/>-->
<!--内部bean 不去id也不会报错,作用域问题-->
<bean id="otherBean01" class="cn.xgl.practice.OtherBean"/>
<bean id="otherBean02" class="cn.xgl.practice.OtherBean"/>
<bean id="otherBean03" class="cn.xgl.practice.OtherBean"/>
</list>
</property>
<!--
注入 Properties props1
Properties是HashTable的子类,但是要求键和值都必须是String
通常情况下,项目中经常用jdbc.properties 解决硬编码问题
每一个prop标签就表示一个键值对
-->
<property name="props1">
<!--支持中文,属性值无乱码-->
<props>
<prop key="driverClassName">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/jpa</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
<prop key="name">张三丰</prop>
</props>
</property>
<!--
Properties props2
还可以直接写一个value标签,然后将一个jdbc.properties文件的文本内容直接写在value标签内部
这个写法不支持中文(不是注释内容,而是等号后面的值)
-->
<property name="props2">
<value>
####注释
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jpa
username=root
password=123456
name=小猪猪
</value>
</property>
</bean>
四 代理模式
简述: 是一种设计模式,其目的是为了降低耦合度,具有良好的扩展性;
4.1 静态代理
简述: B 和 C 分别实现A接口,其中B类对象中持有A对象作为属性,在B重写的方法中调用A类对象的方法,并在其方法前后插入业务逻辑代码;
缺陷: 被代理对象固定,代理业务固定
/**
* 静态代理模式:
* 抽象主体角色: 房源 接口
* 真实主题角色: 房东 实现接口
* 代理主题角色: 中介 实现接口
*
* 代理对象持有真实主体角色对象,在执行真实角色的方法前后可以插入业务逻辑
* 便于扩展
* 缺陷: 被代理对象不能动态改变,代理业务固定
*/
public interface Housing {
void look();//看房子
void sign();//签合同
void rent();//租金
}
public class Intermediary implements Housing{
private Housing housing;
public Intermediary(Housing housing) {
this.housing = housing;
}
@Override
public void look() {
System.out.println("看房前");
housing.look();
System.out.println("看房后");
}
@Override
public void sign() {
System.out.println("签合同前");
housing.sign();
System.out.println("签合同后");
}
@Override
public void rent() {
System.out.println("收租金前");
housing.rent();
System.out.println("收租金后");
}
@Override
public String toString() {
return "Intermediary{" +
"housing=" + housing +
'}';
}
}
public class Landlord implements Housing {
@Override
public void look() {
System.out.println("看房子");
}
@Override
public void sign() {
System.out.println("签合同");
}
@Override
public void rent() {
System.out.println("收租金");
}
}
public class MyInvocationHandler implements InvocationHandler {
//被代理对象
private Housing landlord;
public MyInvocationHandler(Housing landlor) {
this.landlord = landlor;
}
/**
* 调用代理对象的方式时自动执行
* @param proxy
* @param method 被代理对象的方法
* @param args 参数
* @return Objcet
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = method.invoke(landlord, args);
System.out.println("res___"+res);
return res;
}
}
@Test
public void test() throws Exception {
Housing landlord = new Landlord();
Housing intermediary = new Intermediary(landlord);
intermediary.look();
intermediary.sign();
intermediary.rent();
}
/*结果:
看房前
看房子
看房后
签合同前
签合同
签合同后
收租金前
收租金
收租金后*/
4.2 动态代理
简述: 相较静态代理而言,不用手动创建代理类和代理对象,使用JDK的API自动创建代理对象
实体类与上的Housing 和 Landlord一致
public class Dynamic_ProxyTest {
/**
* ClassLoader loader, 类加载器
Class<?>[] interfaces, 被代理对象实现过的所有接口,字节码数组
InvocationHandler h 执行处理器,接口
* @throws Exception
*/
@Test
public void test() throws Exception {
//创建被代理对象
Housing Landlord = new Landlord();
//获取当前类的字节码加载器
ClassLoader loader = Thread.currentThread().getContextClassLoader();
//获取被代理对象实现的接口
Class<?>[] interfaces = Landlord.getClass().getInterfaces();
//将被代理对象作为参数传入,返回一个Objcet对象
MyInvocationHandler handler = new MyInvocationHandler(Landlord);
//JDK动态代理生成一个代理对象(代理类不用自己写其对象也不用创建)
Housing proxy =(Housing)Proxy.newProxyInstance(loader, interfaces, handler);
//执行代理对象方法时,其实就是在执行MyInvocationHandler中的invke方法
//本质上是在执行被代理对象的方法
proxy.look();
System.out.println(proxy); //cn.xgl.static_proxy.Landlord@3581c5f3
System.out.println(proxy.getClass()); //class com.sun.proxy.$Proxy4 ???
}
}
动态代理的原理
/**
* com.sun.proxy.$Proxy4 没有创建却打印了,为什么??
*字符串拼接成一个java类的源代码字符串(持有一个被代理对象作为属性,实现被代理对象实现过的所有接口,重写 接口的方法),保存在内存中
*利用输出流将其输入至$Proxy4.java文件中
*使用动态编译技术,获取字节码文件 .class
*使用类加载器将字节码文件加载到JVM中运行,获取字节码对象
*使用反射技术,获取代理对象, 字节码.newInstance()
*返回该对象
*
*/
五 SpringAop
5.1 xml配置
<!--扫描包-->
<context:component-scan base-package="cn.xgl.spring_aop_xmlconfig.service"/>
<!--模拟事务管理器_切面逻辑类-->
<bean id="tr" class="cn.xgl.spring_aop_xmlconfig.service.TransactionManager"/>
<!--开启自动代理-->
<aop:aspectj-autoproxy/>
<!--
execution__表达式
第一个*____任意返回值类型
第二个*____任意类型
第三个*____任意方法
(..)______参数列表
aop:pointcut_____切点_何地插入业务逻辑
-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* cn.xgl.spring_aop_xmlconfig.service.*.*(..))"/>
<aop:aspect ref="tr">
<!-- 以下配置问题:
当有异常时_开启事务_ 执行xmlConfig_提交事务_回滚事务
发生异常时不能提交事务___解决使用环绕通知
-->
<!--前置通知 切点之前执行begin;
<aop:before method="begin" pointcut-ref="pointcut"/>
后置通知 切点之后执行commit;
<aop:after method="commit" pointcut-ref="pointcut"/>
异常通知 切点异常时执行rollback;
<aop:after-throwing method="rollback" pointcut-ref="pointcut"/>
最终通知 常用于关闭资源;
<aop:after-returning method="close" pointcut-ref="pointcut"/>-->
<aop:around method="around" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
/**
* 模拟事务管理器
* 切面逻辑类
*/
public class TransactionManager {
public void begin() {
System.out.println("开启事务");
}
public void commit() {
System.out.println("提交事务");
}
public void rollback() {
System.out.println("回滚事务");
}
public void close() {
System.out.println("关闭资源");
}
/**
* 环绕通知_自定义执行顺序
* 发生异常时,回滚事务,关闭资源
* @param joinPoint
* @return
*/
public Object around(ProceedingJoinPoint joinPoint){
Object object = null;
try {
this.begin();
joinPoint.proceed();
this.commit();
} catch (Throwable throwable) {
throwable.printStackTrace();
this.rollback();
}finally {
this.close();
}
return object;
}
}
5.2 全注解
<!--扫描包-->
<context:component-scan base-package="cn.xgl.spring_aop_annotation"/>
<!--开启自动代理 全注解必须开启-->
<aop:aspectj-autoproxy/>
@Component //将创建对象的权力交个spring
@Aspect //当前类是一个切面
public class TransactionManager {
/**
* 定义一个切点,以方法名() 为id
*/
@Pointcut("execution(* cn.xgl.spring_aop_annotation.service.*.*(..))")
public void pointcut(){}
//@Before("pointcut()")
public void begin() {
System.out.println("开启事务");
}
//@After("pointcut()")
public void commit() {
System.out.println("提交事务");
}
//@AfterThrowing("pointcut()")
public void rollback() {
System.out.println("回滚事务");
}
//@AfterReturning("pointcut()")
public void close() {
System.out.println("关闭资源");
}
/**
* 环绕通知_自定义执行顺序
* 发生异常时,回滚事务,关闭资源
* @param joinPoint
* @return
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint){
Object object = null;
try {
this.begin();
joinPoint.proceed();
this.commit();
} catch (Throwable throwable) {
throwable.printStackTrace();
this.rollback();
}finally {
this.close();
}
return object;
}
}