springNotes
spring两大核心机制:
- IoC(控制反转)/DI(依赖注入)
- AOP(面向切面编程)
1.IoC
- maven依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
- 创建实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private int age;
}
- 通过IoC创建对象,配置所需的bean,默认通过无参构造函数来获取bean
<bean id="student" class="cn.ty.pojo.Student">
<property name="id" value="1"></property>
<property name="age" value="18"></property>
<property name="name" value="张三"></property>
</bean>
- 通过id获取所需对象
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student)application.getBean("student");
System.out.println(student);
- 通过运行时类获取bean
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student)application.getBean(Student.class);
System.out.println(student);
//这种方式运行时,bean的类只能有一个id即实例对象,否则会报错
- 通过有参构造函数来获取bean
<bean id="student1" class="cn.ty.pojo.Student">
<constructor-arg name="id" value="2"></constructor-arg>
<constructor-arg name="age" value="19"></constructor-arg>
<constructor-arg name="name" value="李四"></constructor-arg>
</bean>
- 给bean中注入集合
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
List<Address> address;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address {
private int id;
private String name;
}
IoC注入
<bean id="address" class="cn.ty.pojo.Address">
<property name="id" value="1"></property>
<property name="name" value="长安"></property>
</bean>
<bean id="address1" class="cn.ty.pojo.Address">
<property name="id" value="2"></property>
<property name="name" value="太湖"></property>
</bean>
<bean id="user" class="cn.ty.pojo.User">
<property name="id" value="1"></property>
<property name="name" value="王五"></property>
<property name="age" value="19"></property>
<property name="address">
<list>
<ref bean="address"></ref>
<ref bean="address1"></ref>
</list>
</property>
</bean>
-
scope作用域
1.singleton:单例,表示通过IoC获取的bean是唯一的,无论业务代码是否获取容器中bean,当加载applicationContext.xml时就会创建bean
2.prototype:原型,表示通过IoC获取的bean时不唯一的,当业务代码获取bean时,才通过无参构造去创建对应的bean
3.request:请求,表示在一次HTTP请求中有效
4.session:回话,表示在一个用户会话内有效 -
spring的继承
与java不同的是,spring的继承是基于对象的继承,子对象包含父对象的所有属性及其属性值,同时还可以添加其他的属性值
<bean id="usr" class="cn.ty.pojo.User" parent="user">
<property name="name" value="小七"></property>
</bean>
User(id=1, name=小七, age=19, address=[Address(id=1, name=长安), Address(id=2, name=太湖)])
- spring的依赖
spring的依赖也是对象与对象之间的关系,配置依赖后,被依赖的bean一定先创建,然后再创建依赖的bean,depends-on - p命名空间
p命名空间是为了更方便的完成bean的配置以及bean之间的依赖注入
<bean id="student3" class="cn.ty.pojo.Student" p:id="3" p:age="20" p:name="小八"></bean> - IoC的自动装载(Autowire)
1. byName 按照属性名来匹配与属性名相同的bean的id
<bean id="user1" class="cn.ty.pojo.User" autowire="byName">
<property name="id" value="2"></property>
<property name="name" value="God"></property>
<property name="age" value="10"></property>
</bean>
User(id=2, name=God, age=10, address=[Address(id=1, name=长安)])
2.byType 按照属性名的类型来匹配与属性类型相同的bean,但是如果存在两个相同类型的bean时会报错
<bean id="user2" class="cn.ty.pojo.User" autowire="byType">
<property name="id" value="3"></property>
<property name="name" value="chief"></property>
<property name="age" value="20"></property>
</bean>
- spring的工厂方法
IoC通过工厂创建的bean有两种方式:
1.静态工厂
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
private int id;
private String name;
}
public class CarFactory {
private static Map<Integer,Car> carMap;
static {
carMap = new HashMap<>();
carMap.put(1, new Car(1, "辉腾"));
carMap.put(2, new Car(2, "凯迪拉克"));
}
public static Car getCar(Integer id){
return carMap.get(id);
}
}
配置工厂
<!--静态工厂创建bean-->
<bean id="car" class="cn.ty.pojo.CarFactory" factory-method="getCar">
<constructor-arg value="2"></constructor-arg>
</bean>
测试:
Car(id=2, name=凯迪拉克)
2.实例工厂
实体类
public class InstanceFactory {
private Map<Integer,Car> carMap;
public InstanceFactory(){
carMap = new HashMap<>();
carMap.put(1, new Car(1, "辉腾"));
carMap.put(2, new Car(2, "凯迪拉克"));
}
public Car getCar(Integer id){
return carMap.get(id);
}
}
配置工厂
<!--实例工厂创建bean-->
<bean id="instanceFactory" class="cn.ty.pojo.InstanceFactory"></bean>
<bean id="car1" class="cn.ty.pojo.Car" factory-bean="instanceFactory" factory-method="getCar">
<constructor-arg value="1"></constructor-arg>
</bean>
测试:
Car(id=1, name=辉腾)
-
AOP
AOP就是将一个类中的某个方法抽象成一个切面对象,动态的将代码切入到切面对象的某个位置
创建个接口
public interface Cal {
public int sum(int num1,int num2);
public int sub(int num1,int num2);
}
实现类
public class CalImpl implements Cal {
@Override
public int sum(int num1, int num2) {
System.out.println("sum"+"的参数是"+num1+"和"+num2);
int result = num1 + num2;
System.out.println("sum"+"的结果是"+result);
return result;
}
@Override
public int sub(int num1, int num2) {
System.out.println("sub"+"的参数是"+num1+"和"+num2);
int result = num1 - num2;
System.out.println("sub"+"的结果是"+result);
return result;
}
}
上述代码中,日志跟代码的耦合度很高,不利于代码的维护,可用AOP来实现优化
1.动态代理实现
给业务代码找个代理,专门做打印日志的工作,这样业务代码只需关注自身的业务
public class ProxyCalImpl implements Cal {
@Override
public int sum(int num1, int num2) {
int result = num1 + num2;
return result;
}
@Override
public int sub(int num1, int num2) {
int result = num1 - num2;
return result;
}
@Override
public int mul(int num1, int num2) {
int result = num1 * num2;
return result;
}
@Override
public int dev(int num1, int num2) {
int result = num1 / num2;
return result;
}
}
动态代理
public class MyInvocationHandler implements InvocationHandler {
//接收委托的对象,就是Cal
public Object object;
//返回代理对象
public Object bind(Object object) {
this.object = object;
//Proxy.newProxyInstance(类加载器,要实现的代理类的接口列表,InvocationHandler类型的对象即要创建动态代理的对象)
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + "的参数是" + Arrays.toString(args));
Object result = method.invoke(this.object, args);
System.out.println(method.getName()+"的结果是"+result);
return result;
}
测试
public static void main(String[] args) {
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
Cal cal = new ProxyCalImpl();
Cal calImpl = (Cal) myInvocationHandler.bind(cal); //注意这里返回的是代理对象的接口,否则报错
calImpl.sum(1, 2);
calImpl.sub(4, 2);
}
}
sum的参数是[1, 2]
sum的结果是3
sub的参数是[4, 2]
sub的结果是2
2. AOP实现
- schema方式实现
1.创建通知类
public class AopBefore implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"执行的方法是"+method.getName());
}
}
public class AopAfter implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"执行的方法是"+method.getName()+"返回值是"+returnValue.toString());
}
}
2.配置Aop
<bean id="before" class="cn.ty.log.AopBefore"></bean>
<bean id="after" class="cn.ty.log.AopAfter"></bean>
<bean id="calImpl" class="cn.ty.utils.impl.ProxyCalImpl"></bean>
<aop:config>
<aop:pointcut id="myPoint" expression="execution(* cn.ty.utils.impl.*.*(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="myPoint"></aop:advisor>
<aop:advisor advice-ref="after" pointcut-ref="myPoint"></aop:advisor>
</aop:config>
3.测试
public static void main(String[] args) {
ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
Cal calImpl = (Cal)application.getBean("calImpl");
calImpl.sum(1, 2);
calImpl.sub(4, 2);
}
结果:
cn.ty.utils.impl.ProxyCalImpl执行的方法是sum
3
cn.ty.utils.impl.ProxyCalImpl执行的方法是sum返回值是3
cn.ty.utils.impl.ProxyCalImpl执行的方法是sub
2
cn.ty.utils.impl.ProxyCalImpl执行的方法是sub返回值是2
- AspectJ方式
1.通知类:
public class MyAdvice {
//前置通知
public void before() {
System.out.println("执行前置通知");
}
//后置通知
public void after(JoinPoint joinPoint, Object result) {
System.out.println(joinPoint.getSignature().getName()+"后置通知");
}
//环绕通知
public Object round(ProceedingJoinPoint joinpoint) throws Throwable {
System.out.println("环绕通知:执行前");
Object proceed = joinpoint.proceed();
System.out.println("环绕通知:执行后");
return proceed;
}
}
2. 配置Aop
<bean id="myAdvice" class="cn.ty.log.MyAdvice"></bean>
<aop:config>
<aop:aspect ref="myAdvice">
<aop:pointcut id="myPoint" expression="execution(* cn.ty.utils.impl.*.*(..)) "/>
<aop:before method="before" pointcut-ref="myPoint" ></aop:before>
<aop:after-returning method="after" pointcut-ref="myPoint" returning="result"></aop:after-returning>
<aop:around method="round" pointcut-ref="myPoint"></aop:around>
</aop:aspect>
</aop:config>
3. 测试:
ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
Cal calImpl = (Cal)application.getBean("calImpl");
calImpl.sum(1, 2);
calImpl.sub(4, 2);
结果:
执行前置通知
环绕通知:执行前
3
环绕通知:执行后
sum后置通知
执行前置通知
环绕通知:执行前
2
环绕通知:执行后
sub后置通知
- 注解配置
1. 在切面类中添加注解:
@Aspect:表示该类是切面类。
@Component:将该类的对象注入到 IoC 容器。
@Aspect
@Component
public class LoggerAspect {
//前置通知
@Before(value = "execution(* cn.ty.utils.impl.*.*(..))")
public void before(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName()+"执行前置通知");
}
//有返回值的后置通知,returning里的值一定要与,方法参数的一致
@AfterReturning(value = "execution(* cn.ty.utils.impl.*.*(..))",returning = "result")
public void after(JoinPoint joinPoint,Object result){
System.out.println(joinPoint.getSignature().getName()+"执行后置通知");
}
@Around(value = "execution(* cn.ty.utils.impl.*.*(..))")
public Object AroundReturning(ProceedingJoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
String arg= Arrays.toString(args);
System.out.println(joinPoint.getSignature().getName()+"执行环绕通知前:参数是"+arg);
Object proceed = null;
try {
proceed = joinPoint.proceed();
System.out.println(joinPoint.getSignature().getName()+"执行环绕通知后:返回值为"+proceed);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return proceed;
}
}
业务类中也要添加 @Component注解
2. 在配置文件中添加配置
<!-- 扫描包 -->
<context:component-scan base-package="cn.ty"></context:component-scan>
<!-- 让 Spring 框架结合切面类和目标类自动生成动态代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
3.测试:
ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
Cal calImpl = (Cal)application.getBean("calImpl");
calImpl.sum(1, 2);
calImpl.sub(4, 2);
结果:
sum执行环绕通知前:参数是[1, 2]
sum执行前置通知
3
sum执行环绕通知后:返回值为3
sum执行后置通知
sub执行环绕通知前:参数是[4, 2]
sub执行前置通知
2
sub执行环绕通知后:返回值为2
sub执行后置通知
-
事务配置
声明式事务配置: <bean id="txManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 哪些方法需要有事务管理 --> <!-- 事务传播机制默认值为:REQUIRED --> <tx:advice id="tx" transaction-manager="txManger"> <tx:attributes> <tx:method name="ins*" propagation="NESTED" isolation="DEFAULT"/> <tx:method name="del*" rollback-for="java.lang.Execption"/> <tx:method name="*" read-only="true" no-rollback-for=""/> </tx:attributes> </tx:advice> <!-- 声明式事务 切点范围设置大点 --> <aop:config> <aop:pointcut expression="execution(* cn.ty.Service.impl.*.*(..))" id="mypoint"/> <aop:advisor advice-ref="tx" pointcut-ref="mypoint"/> </aop:config>

浙公网安备 33010602011771号