一、依赖注入
所谓依赖注入是指容器负责创建对象和维护对象间的依赖关系,而不是通过对象本身负责自己创建和解决自己的依赖。依赖注入的主要目的是解耦,体现了一种“组合”的思想。
Spring IoC(Application Context)负责创建Bean,并通过容器将功能类Bean注入到你需要的Bean中。Spring提供xml、注解、java配置、groovv配置实现Bean的创建和注入。
声明Bean的注解:
- @Component,没有明确的角色。
- @Service在业务逻辑层(service层)使用。
- @Repository在数据访问层使用(dao层)使用。
- @Controller在表现层(MVC-Spring MVC)使用。
注入Bean的注解,一般情况下通用。
- @Autowired:Spring提供的注解。
- @Inject:JSR-330提供的注解。
- @Resource:JSR-250提供的注解。
以上三个注入Bean的注解可以用在set()方法上或者属性上。
- @Configuration声明当前类是一个配置类。
- @ComponentScan,自动扫描包名下所有使用@Service、@Component、@Repository和@Controller的类,并注册成为Bean。
下面来看一个具体的例子(代码来自spring boot实战):
@Configuration //使用Configuration声明当前类是一个配置类
@ComponentScan("com.wisely.highlight_spring4.ch1.di") //2
public class DiConfig {
}
@Service //使用service注解声明当前FunctionService类是Spring管理的一个Bean。
public class FunctionService {
public String sayHello(String word){
return "Hello " + word +" !";
}
}
@Service //声明为Bean
public class UseFunctionService {
@Autowired //将FunctionService实体Bean注入到UserFuncitonService中。
FunctionService functionService;
public String SayHello(String word){
return functionService.sayHello(word);
}
}
public class Main {
public static void main(String[] args) {
//使用AnnotationConfigApplication作为Spring容器,接收一个配置类作为参数。
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(DiConfig.class);
//通过容器获取Beam
UseFunctionService useFunctionService = context.getBean(UseFunctionService.class);
System.out.println(useFunctionService.SayHello("world"));
context.close();
}
}
//输出
Hello world !
注释已经比较清楚啦,利用spring容器获取bean,并实现了自动注入。
二、Java配置
Java配置是Spring 4.x推荐的配置方式,可以完全替代xml配置,Java配置也是Spring Boot推荐的配置方式。Java配置是通过@Configuration和@Bean实现的。
一帮全局配置使用Java配置(如数据库相关配置、MVC相关配置),业务Bean配置使用注解配置。
三、面向切面——AOP
AOP可以让一组类共享相同的行为。Spring支持AspectJ的注解式切面编程。
- 使用@Aspect声明式一个切面。
- 使用@After、@Before、@Around定义建言(advice),可以直接将拦截规则(切点)作为参数。
- 其中@After、@Before、@Around参数的拦截规则为切点(PointCut),为了是切点复用,可使用@PointCut专门定义拦截规则,然后在@After、@Before、@Around的参数中调用。
- 其中符合条件的每一个拦截处为连接点(JoinPoint)。
下面看Spring boot实战中一个具体的例子。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Action {
String name();
}
这个类是一个注解类,我们将其作为拦截器规则的注解。
- @Target标签表明该注解用于什么地方,可能的值在枚举类ElementType中,具体包括:
- ElementType.CONSTRUCTOR
- ElementType.FIELD
- ElementType.LOCAL_VARIABLE
- ElementType.METHOD
- …
- @Retention表示在什么级别保存该注解信息。可选的参数值在枚举类型RetentionPolicy中。
- RetentionPolicy.SOURCE 注解将被编译器丢弃。
- RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃。
- RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。
- @Documented将此注解包含在javadoc中,它代表着此注解会被javadoc工具提取成文档。
@Configuration
@ComponentScan("com.wisely.highlight_spring4.ch1.aop")
@EnableAspectJAutoProxy
public class AopConfig {
}
- @Configuration声明当前类是一个配置类。
- @ComponentScan(“com.wisely.highlight_spring4.ch1.aop”)用于扫描包下所有的bean,并通过spring容器管理。
- @EnableAspectJAutoProxy允许面向切面的自动代理,这样spring启动时会自动扫描AOP相关的标注,在创建对象时帮我们去织入过程。
@Service
public class DemoAnnotationService {
@Action(name="注解式拦截的add操作")
public void add(){
System.out.println("DemoAnnotationService");
}
}
这个类在add方法前使用了前面定义的@Action注解。
Service
public class DemoMethodService {
public void add(){
System.out.println("DemoMethodService");
}
}
这个类并没有使用前面定义的注解。
@Aspect //声明为一个切面
@Component //成为bean
public class LogAspect {
//通过Pointcut注解声明切点
//切入点表达式,决定哪个函数具体执行。
@Pointcut("@annotation(com.wisely.highlight_spring4.ch1.aop.Action)")
public void annotationPointCut() {} //切入点签名,包含名称和任意参数,这里参数为空。
@After("annotationPointCut()") //通过after注解声明一个建言,并使用pointcut定义的切点
public void after(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Action action = method.getAnnotation(Action.class);
System.out.println("注解式拦截 " + action.name()); //通过反射可以获得注解上的属性,然后做日志相关操作。
}
@Before("execution(* com.wisely.highlight_spring4.ch1.aop.DemoMethodService.*(..))") //此建言直接使用拦截规则作为参数。
public void before(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("方法规则式拦截," + method.getName());
}
}
这个类就是我们编写的切面类,具体过程如下:
- @Aspect先声明为类一个切面,并通过@Component声明为bean。
- 在类内声明切点,并定义好签名和参数。
- @After注解声明一个建言,并使用pointcut定义的切点。
- 建言(advice)就是一个在切点上运行的方法。
- @Before注解声明一个建言。
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AopConfig.class);
DemoAnnotationService demoAnnotationService = context.getBean(DemoAnnotationService.class);
DemoMethodService demoMethodService = context.getBean(DemoMethodService.class);
demoAnnotationService.add();
demoMethodService.add();
context.close();
}
}
//输出
DemoAnnotationService
注解式拦截 注解式拦截的add操作
方法规则式拦截,add
DemoMethodService
可以看到已经成功织入,切点上执行了建言。