Spring基础使用
Spring IOC
- 新建工程
新建maven工程,然后在pom.xml中添加如下依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
- 测试IOCbean注入
新建三个服务类:ServiceA, ServiceB, ServiceC
package com.test.service;
/**
* @author felix
*/
public class ServiceA {
private ServiceB serviceB;
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
public void doSth(){
System.out.println("serviceA doSth");
}
}
package com.test.service;
/**
* @author felix
*/
public class ServiceB {
private ServiceC serviceC;
public void setServiceC(ServiceC serviceC) {
this.serviceC = serviceC;
}
public void doSth() {
System.out.println("ServiceB doSth");
}
}
package com.test.service;
/**
* @author felix
*/
public class ServiceC {
private ServiceA serviceA;
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
public void doSth() {
System.out.println("ServiceC doSth");
}
}
新增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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="serviceA" class="com.test.service.ServiceA">
<property name="serviceB" ref="serviceB"/>
</bean>
<bean name="serviceB" class="com.test.service.ServiceB">
<property name="serviceC" ref="serviceC"/>
</bean>
<bean name="serviceC" class="com.test.service.ServiceC">
<property name="serviceA" ref="serviceA"/>
</bean>
</beans>
注意:bean的依赖项配置在property标签中,同时需要再依赖方定义成员变量,并写好setter方法,如果不生成setter,Spring默认是无法注入进来的
新增测试类:
package com.test;
import com.test.service.ServiceA;
import com.test.service.ServiceB;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringFrameworkTests {
private ApplicationContext applicationContext;
@Before
public void init() {
applicationContext = new ClassPathXmlApplicationContext("beans.xml");
}
@Test
public void testIoc() {
ServiceA serviceA = (ServiceA) applicationContext.getBean("serviceA");
serviceA.doSth();
ServiceB serviceB = applicationContext.getBean(ServiceB.class);
serviceB.doSth();
}
}
执行单元测试,结果如下:
serviceA doSth
ServiceB doSth
- 属性注入
在beans.xml中新增如下配置:
<context:property-placeholder location="db.properties"/>
<bean name="config" class="com.test.service.Config">
<property name="driverClass" value="${db.driverClass}"/>
<property name="jdbcUrl" value="${db.jdbcUrl}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="ccc"/>
<property name="infoMap" ref="infoMap"/>
<property name="properties" ref="properties"/>
</bean>
<util:map id="infoMap">
<entry key="a" value="a"/>
<entry key="b" value="b"/>
</util:map>
<util:properties id="properties">
<prop key="p1">1</prop>
<prop key="p2">c</prop>
</util:properties>
新增db.propertis
db.driverClass=com.mysql.jdbc.Driver
db.jdbcUrl=///spring-learning
db.username=root
db.password=root
新增Config.java
package com.test.service;
import java.util.Map;
import java.util.Properties;
public class Config {
private String driverClass;
private String jdbcUrl;
private String username;
private String password;
private Map<String, Object> infoMap;
private Properties properties;
@Override
public String toString() {
return "Config{" + "driverClass='" + driverClass + '\'' + ", jdbcUrl='" + jdbcUrl + '\''
+ ", username='" + username + '\'' + ", password='" + password + '\'' + ", infoMap="
+ infoMap + ", properties=" + properties + '}';
}
public String getDriverClass() {
return driverClass;
}
public void setDriverClass(String driverClass) {
this.driverClass = driverClass;
}
public String getJdbcUrl() {
return jdbcUrl;
}
public void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Map<String, Object> getInfoMap() {
return infoMap;
}
public void setInfoMap(Map<String, Object> infoMap) {
this.infoMap = infoMap;
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
新增测试方法:
@Test
public void testIocProperties() {
Config config = applicationContext.getBean(Config.class);
System.out.println(config.toString());
}
执行结果:
Config{driverClass='com.mysql.jdbc.Driver', jdbcUrl='///spring-learning', username='root', password='ccc', infoMap={a=a, b=b}, properties={p2=c, p1=1}}
Spring可以注入的属性值有:
基础数据类型,bean对象,Properties, Map, Set等,其中Properties,Map,Set我们需要用对应的标签单独配置,property 的value部分填入对应的id即可。
测试AOP
在beans.xml中新增如下配置:
<bean id="logUtil" class="com.test.aop.LogUtil"/>
<aop:config>
<aop:aspect id="logAdvice" ref="logUtil">
<aop:pointcut id="doSthPointCut"
expression="execution(public * com.test.service.ServiceA.doSth())"/>
<aop:before method="printBefore"
pointcut-ref="doSthPointCut"/>
<aop:after method="printAfter"
pointcut-ref="doSthPointCut"/>
<aop:after-returning method="printAfterReturning"
pointcut-ref="doSthPointCut"/>
<aop:after-throwing method="afterThrowing"
pointcut-ref="doSthPointCut"/>
<aop:around method="around" pointcut-ref="doSthPointCut"/>
</aop:aspect>
</aop:config>
新增LogUtil.java
package com.test.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.Pointcut;
public class LogUtil {
/**
* 前面打印
*/
public void printBefore() {
System.out.println("print info from LogUtil.printBefore");
}
/**
* 后面打印
*/
public void printAfter() {
System.out.println("print info from LogUtil.printAfter");
}
/**
* 后面打印
*/
public void printAfterReturning() {
System.out.println("print info from LogUtil.printAfterReturning");
}
/**
* 后面打印
*/
public void afterThrowing() {
System.out.println("print info from LogUtil.afterThrowing");
}
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---around before----");
Object result = joinPoint.proceed();
System.out.println(result);
System.out.println("---around after----");
}
}
改造serviceA的doSth方法:
public String doSth(){
System.out.println("serviceA doSth");
// throw new RuntimeException("an exception ");
return "hello";
}
启动第一步中测试IOC BEAN的测试方法
@Test
public void testSimpleIoc() {
ServiceA serviceA = (ServiceA) applicationContext.getBean("serviceA");
serviceA.doSth();
ServiceB serviceB = applicationContext.getBean(ServiceB.class);
serviceB.doSth();
}
输出结果如下:
print info from LogUtil.printBefore
---around before----
serviceA doSth
hello
---around after----
print info from LogUtil.printAfterReturning
print info from LogUtil.printAfter
ServiceB doSth
在ServiceA.doSth中抛出异常
public String doSth(){
System.out.println("serviceA doSth");
throw new RuntimeException("an exception ");
// return "hello";
}
执行结果如下:
print info from LogUtil.printBefore
---around before----
serviceA doSth
print info from LogUtil.afterThrowing
print info from LogUtil.printAfter
java.lang.RuntimeException: an exception
一堆异常信息。。。
- aspec指定切面逻辑类,也即是使用被用来处理切面的逻辑达到增强其他方法的目的那个类
- pointcut指定切入点是什么,往往是某个类或者某些类的某些方法。也即是配置我们哪些类的哪些方法需要被增强。
- before, after, afterReturning, afterThrowing, around 分别表示横切逻辑代码所执行的时机。也即我们希望在目标方法执行的什么时候进行增强。
从测试结果来分析,这几个切入时机执行顺序分别为:
before > around前置处理 > 目标方法逻辑(around joinPoint.proceed) > around后置处理>afterReturing / afterThrowing > after
注:afterReturning与afterThrowing只会执行其中之一,因为目标逻辑代码要么正常执行完成,要么出现异常
Spring基于注解的开发
修改测试代码前两部分如下:
@Before
public void init() {
// applicationContext = new ClassPathXmlApplicationContext("beans.xml");
applicationContext = new AnnotationConfigApplicationContext(BootConfig.class);
}
在ServiceA,ServiceB,ServiceC类上分别加上@Service注解,setter方法参数前面加上@Autowired注解,ServiceA如下:
@Service
public class ServiceA {
private ServiceB serviceB;
public void setServiceB(@Autowired ServiceB serviceB) {
this.serviceB = serviceB;
}
public String doSth(){
System.out.println("serviceA doSth");
// throw new RuntimeException("an exception ");
return "hello";
}
}
启动测试方法,执行结果如下:
serviceA doSth
ServiceB doSth
Bean注解
- @Component 声明当前Class为一个ioc bean,Spring扫包的时候会将其作为一个需要管理的bean来解析并实例化
- @Service
- @Controller
- @Repository
本质上后面三个注解与@Component一样的效果,只不过为了区分代码层级,提供了后面三个注解。 - @Autowired 表明某个属性需要ioc容器来注入,spring通过类型来找到需要注入的bean, 同时可以通过@Qualifier("beanName")来通过名称注入。
用AOP注解改造LogUtil.java
package com.test.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
@Component
public class LogUtil {
@Pointcut("execution(public * com.test.service.ServiceA.doSth())")
public void pointCut() {
}
/**
* 前面打印
*/
@Before("com.test.aop.LogUtil.pointCut()")
public void printBefore() {
System.out.println("print info from LogUtil.printBefore");
}
/**
* 后面打印
*/
@After("com.test.aop.LogUtil.pointCut()")
public void printAfter() {
System.out.println("print info from LogUtil.printAfter");
}
/**
* 后面打印
*/
@After("com.test.aop.LogUtil.pointCut()")
public void printAfterReturning() {
System.out.println("print info from LogUtil.printAfterReturning");
}
/**
* 后面打印
*/
@AfterThrowing("com.test.aop.LogUtil.pointCut()")
public void afterThrowing() {
System.out.println("print info from LogUtil.afterThrowing");
}
@Around("com.test.aop.LogUtil.pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---around before----");
Object result = joinPoint.proceed();
System.out.println(result);
System.out.println("---around after----");
return result;
}
}
改造SpringConfig.java
/**
* @author felix
*/
@Configuration
@ComponentScan(basePackages = "com.test")
@EnableAspectJAutoProxy
public class BootConfig {
}
启动测试,结果如下:
---around before----
print info from LogUtil.printBefore
serviceA doSth
print info from LogUtil.printAfterReturning
print info from LogUtil.printAfter
hello
---around after----
ServiceB doSth
AOP注解开发,需要@EnableAspectJAutoProxy开启
用@Aspec生命切面类,@PointCut定义一个切入点,@Before,@After,@AfterThrowing,@After,@Around分别定义切入时机