IOC控制反转
概念:org.springframework.context.ApplicationContext接口代表 Spring IoC 容器,**负责实例化、配置和组装 bean。**容器通过读取配置元数据来获取有关要实例化、配置和组装哪些对象的指令。配置元数据以 XML、Java 注释或 Java 代码表示。IOC是一种思想,依赖注入DI是实现spring的ioc容器的一种方式。
IOC容器可以帮助实例化对象,通过注入的方式。
注入可以分为:
- 构造器注入
- 依赖注入(set方式进行注入)
- 拓展注入
c命名空间
p命名空间
分别在spring配置文件中添加配置xmlns:c="http://www.springframework.org/schema/c" xmlns:p="http://www.springframework.org/schema/p"
IOC案例
<!--配置自动装配
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
-->
<!--开启自动装配 <context:annotation-config/>-->
<!--引入p命名空间 xmlns:p="http://www.springframework.org/schema/p"-->
<!--引入c命名空间 xmlns:c="http://www.springframework.org/schema/c"-->
<!--导入其他资源包 <import resource="beans.xml"/>-->
<!--注册bean-->
- 配置环境maven
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
- 编写Java实体类
Pet.java
package cn.supperbro.domain;
/**
* 一个pet实体类
*/
public class Pet {
private String name;
private int age;
public Pet() {
}
public Pet(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Pet{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
User.java
package cn.supperbro.domain;
import java.util.*;
/**
* 一个user实体类
*/
public class User {
/**
* 包含基本类型,集合类型,数组,对象
*/
private String name;
private String[] nick;
private List<String> hobbies;
private Map<Object,Object> friends;
private Set<String> telephone;
private Properties address;
private Pet pet;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getNick() {
return nick;
}
public void setNick(String[] nick) {
this.nick = nick;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
public Map<Object, Object> getFriends() {
return friends;
}
public void setFriends(Map<Object, Object> friends) {
this.friends = friends;
}
public Set<String> getTelephone() {
return telephone;
}
public void setTelephone(Set<String> telephone) {
this.telephone = telephone;
}
public Properties getAddress() {
return address;
}
public void setAddress(Properties address) {
this.address = address;
}
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.pet = pet;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", nick=" + Arrays.toString(nick) +
", hobbies=" + hobbies +
", friends=" + friends +
", telephone=" + telephone +
", address=" + address +
", pet=" + pet +
'}';
}
}
- 配置applicationContext.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用有参构造器注入pet-->
<bean id="pet" class="cn.supperbro.domain.Pet">
<!--可以选择index下标 name type参数类型进行参数赋值-->
<constructor-arg index="0" value="喵喵"/>
<constructor-arg name="age" value="2"/>
</bean>
<!--使用依赖注入user-->
<bean id="user" class="cn.supperbro.domain.User">
<property name="name" value="明天"/>
<!--数组赋值-->
<property name="nick">
<array>
<value>小明</value>
<value>小天</value>
</array>
</property>
<!--list集合-->
<property name="hobbies">
<list>
<value>篮球*1</value>
<value>篮球*2</value>
</list>
</property>
<!--map集合-->
<property name="friends">
<map>
<entry key="好朋友" value="今天"/>
<entry key="女朋友" value="昨天"/>
</map>
</property>
<!--set集合-->
<property name="telephone">
<set>
<value>1233654789</value>
<value>zxcedfsfsd</value>
</set>
</property>
<!--prop-->
<property name="address">
<props>
<prop key="新地址">china</prop>
<prop key="旧地址">old china</prop>
</props>
</property>
<!--对象 通过引用-->
<property name="pet" ref="pet"/>
</bean>
</beans>
- 测试
import cn.supperbro.domain.Pet;
import cn.supperbro.domain.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
@org.junit.Test
public void testBean(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Pet pet = context.getBean("pet", Pet.class);
User user = context.getBean("user", User.class);
System.out.println(pet);
System.out.println("-------------------------");
System.out.println(user);
}
/*
Pet{name='喵喵', age=2}
-------------------------
User{name='明天', nick=[小明, 小天], hobbies=[篮球*1, 篮球*2],
friends={好朋友=今天, 女朋友=昨天}, telephone=[1233654789, zxcedfsfsd],
address={新地址=china, 旧地址=old china}, pet=Pet{name='喵喵', age=2}}
*/
}
通过注解也可以配置
在spring4之后,要使用注解开发,必须要导入aop的包,使用注解导入context约束。可以配置注解扫描的包
<context:component-scan base-package="cn.supperbro.domain"/>
-
bean
@Component :组件 ;作用于类上;相当于创建一个bean
@Value:作用于属性上或者是set方法上,相当于给bean赋值
-
衍生的注解
MVC三层架构分层
Dao:@Repository
Service:@Service
Controller:@Colltroller
功能都是一样的,将其作用的类注册到Spring的IOC容器中,装配
-
@Scope("…")作用在类上,定义其作用域
package cn.supperbro.domain;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Student {
@Value("supperbro")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
总结
简单介绍到了IOC的基本使用,当然还有bean的作用域,自动装配,注解开发等等…
参照官网吧
AOP面向切面
代理模式
静态代理
当然在使用spring的aop进行切面编程也是要进行方法增强必须要了解代理模式啦
角色分析:
1. 抽象角色:一般会通过接口或者抽象类
2. 真实角色:被代理的角色
3. 代理角色:代理真实角色,一般带有附属属性
4. 客户端角色:访问代理角色
代理模式的好处:
- 可以是真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共任务交给代理角色,实现了业务的分工
- 公共业务发生了拓展时,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,代码量大。
动态代理
动态代理和静态代理角色一样
动态代理的代理类时自动生成的
动态代理:
- 基于接口实现的动态代理—JDK的动态代理
- 基于类的动态代理—cglib/Java字节码JAVAAssis
通过InvocationHandler代码实现动态代理
- 抽象对象
package cn.supperbro.proxy;
public interface Sent {
public void sent();
}
- 真实对象
package cn.supperbro.proxy;
public class Host implements Sent{
@Override
public void sent() {
System.out.println("房东要卖房子...");
}
}
- 动态代理生成代理类
package cn.supperbro.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest implements InvocationHandler {
//定义要被代理的抽象角色
private Object abstractCharacter;
//set方法赋值
public void setAbstractCharacter(Object abstractCharacter) {
this.abstractCharacter = abstractCharacter;
}
public ProxyTest() {
}
//或者有参构造赋值
public ProxyTest(Object abstractCharacter) {
this.abstractCharacter = abstractCharacter;
}
//生成代理类 需要用到类Proxy
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), abstractCharacter.getClass().getInterfaces(),this);
}
//执行生成代理类的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(abstractCharacter, args);
add();//方法需要添加才可以被使用
return invoke;
}
public void add(){
System.out.println("代理类自带的方法");
}
}
- 客户端
package cn.supperbro.proxy;
//客户端
public class Client {
public static void main(String[] args) {
//创建真实对象
Host host = new Host();
//使用动态生成器进行代理类的生成
ProxyTest pid = new ProxyTest(host);
//生成代理类
Sent proxy = (Sent) pid.getProxy();
proxy.sent();
}
}
也可以注册到IOC中…
AOP概念
AOP(Aspect Orient Programming)也就是面向切面编程,作为面向对象编程的一种补充,已经成为一种比较成熟的编程方式。其实AOP问世的时间并不太长,AOP和OOP互为补充,面向切面编程将程序运行过程分解成各个切面。AOP专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在JavaEE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,AOP已经成为一种非常常用的解决方案。
- 切面(Aspect): 切面用于组织多个Advice,Advice放在切面中定义。
- 连接点(Joinpoint): 程序执行过程中明确的点,如方法的调用,或者异常的抛出。在Spring AOP中,连接点总是方法的调用。
- 增强处理(Advice): AOP框架在特定的切入点执行的增强处理。处理有"around"、"before"和"after"等类型
- 切入点(Pointcut): 可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。
实现方法
在实现AOP测试入门案例时,方法增强作用于日志输出,所以会用到log4j日志。
环境配置
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
log4j的配置文件
# Root logger option
log4j.rootLogger=DEBUG,console,file
# 输出到控制台的相关的配置
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
# 输出到文件的相关配置
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.file=./log/log.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出的级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
方式一通过实现接口的方式
- 切点类
接口 ServiceTest.java
package cn.supperbro.service;
public interface ServiceTest {
void add();
void delete();
}
实现类 ServiceTestImpl.java
package cn.supperbro.service.impl;
import cn.supperbro.service.ServiceTest;
public class ServiceTestImpl implements ServiceTest {
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
}
- 方法增强
创建切面类 AddLog.java
通过实现接口来实现AOP
package cn.supperbro.log;
import org.apache.log4j.Logger;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AddLog implements AfterReturningAdvice {
//实现接口的方式:afterReturningAdvice表示的是切点方法之后执行该方法
/**
*
* @param o 切点对象返回的值
* @param method 切点的方法
* @param objects args参数
* @param o1 表示切点本身
* @throws Throwable
*/
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
Logger logger = Logger.getLogger(AddLog.class);
logger.info(o1.getClass().getName()+"执行了"+method.getName()+"方法"+";返回的参数是:"+o);
}
}
- 配置
关于execution表达式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="serviceTest" class="cn.supperbro.service.impl.ServiceTestImpl"/>
<bean id="addLog" class="cn.supperbro.log.AddLog"/>
<aop:config>
<!--切入点-->
<aop:pointcut id="testPoint" expression="execution(* cn.supperbro.service.impl.ServiceTestImpl.*(..))"/>
<!--引入方法增强切面-->
<aop:advisor advice-ref="addLog" pointcut-ref="testPoint"/>
</aop:config>
</beans>
- 测试
public class Test {
@org.junit.Test
public void testBean(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
ServiceTest test = context.getBean("serviceTest", ServiceTest.class);
test.add();
}
/*增加了一个用户
[cn.supperbro.log.AddLog]-cn.supperbro.service.impl.ServiceTestImpl执行了add方法;返回的参数是:null*/
}
实现方式二
自定义切面 功能相对于实现接口的功能较差。
切面
package cn.supperbro.log;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.runtime.internal.AroundClosure;
/**
* 自定义切面
*/
public class DivProxy {
public void after(){
Logger logger = Logger.getLogger(DivProxy.class);
logger.info("在方法执行以后执行");
}
//环绕增强
Logger logger = Logger.getLogger(DivProxy.class);
public Object around(ProceedingJoinPoint point) throws Throwable {
logger.info("在方法执行前");
//切入点
Object proceed = point.proceed();
logger.info("在方法执行后");
return proceed;
}
}
配置
<aop:config>
<!--切入点-->
<aop:pointcut id="testPoint" expression="execution(* cn.supperbro.service.impl.ServiceTestImpl.*(..))"/>
<!--
通知-->
<aop:aspect id="div" ref="divProxy">
<aop:around method="around" pointcut-ref="testPoint"/>
<aop:after method="after" pointcut-ref="testPoint"/>
</aop:aspect>
</aop:config>
测试
@org.junit.Test
public void test2(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// DivProxy divProxy = context.getBean("divProxy", DivProxy.class);
ServiceTest test = context.getBean("serviceTest", ServiceTest.class);
test.delete();
test.add();
}
/*
[cn.supperbro.log.DivProxy]-在方法执行前
删除了一个用户
[cn.supperbro.log.DivProxy]-在方法执行后
[cn.supperbro.log.DivProxy]-在方法执行以后执行
[cn.supperbro.log.DivProxy]-在方法执行前
增加了一个用户
[cn.supperbro.log.DivProxy]-在方法执行后
[cn.supperbro.log.DivProxy]-在方法执行以后执行
*/
}
方式三 通过注解配置
- 创建接口和目标类
```java
package cn.supperbro.annotation;
public interface Target {
void add();
}
package cn.supperbro.annotation;
import org.springframework.stereotype.Component;
@Component("target")
public class TargetImpl implements Target {
@Override
public void add() {
System.out.println("增加了");
}
}
- 创建切面类
- 将目标和切面类的对象创建权交给spring
- 在切面类中进行使用注解配置织入关系
package cn.supperbro.annotation;
import org.apache.log4j.Logger;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component("annotation")
@Aspect
public class ProxyAnno {
@Before("execution(* cn.supperbro.annotation.TargetImpl.*(..))")
public void before(){
Logger logger = Logger.getLogger(ProxyAnno.class);
logger.info("方法前");
}
}
- 在配置文件中开启组件扫描和AOP的自动代理
<context:component-scan base-package="cn.supperbro.annotation"/>
<!--aop自动代理-->
<aop:aspectj-autoproxy/>
- 测试
@org.junit.Test
public void testBean(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Target target = (Target) context.getBean("target");
target.add();
}
/*
[cn.supperbro.annotation.ProxyAnno]-方法前
增加了
*/