Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架
Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日,发布了1.0正式版本
Rod Johnson :Spring Framework创始人
spring理念:使现有的技术更加容易使用,本身是一个大杂烩
Spring 融合件
优点:
Spring是一个开源的免费的框架(容器)!
Spring是一个轻量级,非入侵式的框架
控制反转(IoC) 面向切面(AOP)
支持事务的处理,对框架整合的支持!
缺点:
发展了太长时间,违背了原来的理念你!配置十分繁琐。人称:配置地狱
总结:Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架
拓展
Spring Boot
一个快速开发的脚手架
基于Spring Boot可以快速的开发单个微服务
约定大于配置
Spring Cloud
基于Spring Boot实现的
学习Spring Boot的前提,需要完全掌握Spring及SpringMVC!承上启下的作用
控制反转:
public class UserServiceImpl implements UserService { private UserDao userDao; //使用set进行动态实现值的注入 public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void getUser() { userDao.getUser(); } }
之前,程序是主动创建对象!控制权在程序员手上!
使用set注入后,程序不在具有主动性,而是变成了被动的接受对象!
这种思想,从本质上解决了问题,我们程序员不用去管理对象的创建了。系统的耦合性大大降低,可以更加专注的在业务的实现上!这时IOC的原型!
ps:个人总结,通过set方法注入,只需在调用ServiceImpl方法时new一个dao层的方法即可代替之前重复创建Service和ServiceImpl的大量重建
额外:
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从loc容器中取出需要的对象
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法
控制反转是一种通过从描述(XML或注解)并通过第三方去生产或获取特定对象的方法。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)
所谓IoC,对象由Spring来创建,管理,装配!
DAO:
public interface UserDao { void getUser(); } public class UserDaoImpl implements UserDao{ public void getUser() { System.out.println("UserDao执行"); } } public class UserDaoMysqlImpl implements UserDao{ public void getUser() { System.out.println("Mysql处理"); } }
Service:
public interface UserService { void getUser(); } public class UserServiceImpl implements UserService { private UserDao userDao; //使用set进行动态实现值的注入 //可见即使有两个DaoImpl,ServiceImpl通过set注入,一个即可 public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void getUser() { userDao.getUser(); } }
在resource中配置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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="mysqlImpl" class="com.person.dao.UserDaoMysqlImpl" /> <bean id="impl" class="com.person.dao.UserDaoImpl" /> <bean id="UserServiceImpl" class="com.person.service.UserServiceImpl"> <!-- value:具体的值,基本数据类型 ref:引用Spring容器中创建好的对象 --> <property name="userDao" ref="mysqlImpl" /> </bean> </beans>
这里ref为mysqlImpl,绑定的就是UserDaoMysqlImpl
测试代码:
@Test public void getUser() { //获取ApplicationContext; 拿到Spring容器 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //有了容器,需要什么可以直接get UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl"); userServiceImpl.getUser(); }
这里执行后输出就为“Mysql处理”
这里ref改为impl,测试代码不变
输出为“UserDao执行”
类:
public class Hello { private String str; public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return "Hello{" + "str='" + str + '\'' + '}'; } }
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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 使用Spring来创建对象,在Spring这些都称为Bean bean=对象 new Hello(); --> <bean id="hello" class="com.person.pojo.Hello"> <property name="str" value="hello" /> </bean> </beans>
这里的value就是hello的str属性
测试代码:
public class HelloTest { @Test public void MyTest() { //获取Spring的上下文对象 ApplicationContext context=new ClassPathXmlApplicationContext("beans.xml"); //我们的对象都在Spring中的管理了,我们要使用直接去里面取出来就可以了 Hello hello = (Hello) context.getBean("hello"); System.out.println(hello); } }
输出:hello
输出的就是beans.xml的值
先来两个实体类:
public class User { private String name; public User() { System.out.println("User的无参构造!"); } public User(String name) { this.name=name; System.out.println(name+"的有参构造!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "name=" + name ; } } public class UserT { private String name; public UserT() { System.out.println("UserT的无参构造!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "name=" + name ; } }
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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--后面不同方法需要的xml--> </beans>
beans.xml创建对象
<bean id="user" class="com.person.pojo.User" > <!--无参构造--> <property name="name" value="忧郁蓝" /> </bean> <bean id="userT" class="com.person.pojo.UserT"> </bean>
测试代码:
public void test2() { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); User user=(User) context.getBean("user"); System.out.println(user); } /*输出: User的无参构造! -创建对象无参构造自动运行 UserT的无参构造! name=忧郁蓝 - 默认注入值成功---依赖于set注入 */ @Test public void testT() { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserT usert=(UserT) context.getBean("userT"); System.out.println(usert); } /* 输出: User的无参构造! UserT的无参构造! name=null 默认没有注入 */
beans.xml配置
<beanid="user" class="com.person.pojo.User" > <!-- 有参下标赋值--> <constructor-arg index="0" value="勇者无敌" /> </bean>
测试代码:
@Test public void test2() { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); User user=(User) context.getBean("user"); System.out.println(user); } /*输出: 勇者无敌的有参构造! name=勇者无敌 */
只改bean就行,测试代码不变
<bean id="user" class="com.person.pojo.User" > <constructor-arg type="java.lang.String" value="第二种方式类型赋值" /><!--有参类型赋值(不推荐使用)--> </bean> <!--输出: 第二种方式类型赋值的有参构造! name=第二种方式类型赋值 -->
<beanid="user" class="com.person.pojo.User" > <constructor-arg name="name" value="第三种方式" /><!--直接通过参数名来设置--> </bean> <!--输出: 第三种方式的有参构造! name=第三种方式 -->
IoC创建对象的方式: 1.使用无参构造创建对象,默认! beans.xml配置: <bean id="user" class="com.User" > <property name="name" value="无敌溜溜球" /> </bean> 实例化: ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); User user=(User) context.getBean("user"); 2.假设我们要使用有参构造创建对象 第一种方式:下标赋值 实体类: public User(String name) { this.name=name; System.out.println(name+"的有参构造!"); } beans.xml配置 <bean id="user" class="com.User" > <constructor-arg index="0" value="勇者无敌" /> </bean> 实例化同上 第二种方式:类型赋值(不推荐使用) beans.xml配置 <bean id="user" class="com.User" > <constructor-arg type="java.lang.String" value="第二种方式类型赋值" /> </bean> 第三种方式:直接通过参数名来设置 beans.xml配置 <bean id="user" class="com.User" > <constructor-arg name="name" value="第三种方式" /> </bean> 总结:在配置文件加载的时候,容器中管理的对象就已经初始化了! 从spring容器中取出的对象都是同一个
实体类:
public class UserT { private String name; public UserT() { System.out.println("UserT的无参构造!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "name=" + name ; } }
beans.xml配置
<!--别名,如果添加了别名,我们也可以使用别名获取到这个对象--> <alias name="userT" alias="abc" /> <bean id="userT" class="com.person.pojo.UserT" > <property name="name" value="UserT测试" /> </bean>
测试代码
@Test public void testT() { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserT usert=(UserT) context.getBean("abc"); System.out.println(usert); } /*输出: UserT的无参构造! name=UserT测试 */
beans.xml
<!-- id:bean的唯一标识符,也就是相当于我们学的对象名 class:bean对象所对应的全限定名:包名+类型 name:也是别名,而且name可以同时取多个别名,逗号,分号和空格等都可以分割别名 scope:作用域 --> <bean id="userT" class="com.person.pojo.UserT" name="userT2,u2 u3;u4"> <property name="name" value="UserT测试" /> </bean>
测试代码
@Test public void testT() { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserT usert=(UserT) context.getBean("u4"); System.out.println(usert); } /*输出: UserT的无参构造! name=UserT测试 */
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
在beans.xml同级目录下创建beans2.xml,beans3.xml,
然后创建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"> <import resource="beans.xml" /> <import resource="beans2.xml" /> <import resource="beans3.xml" /> </beans>
测试代码也可以正常运行
@Test public void testT2() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserT usert=(UserT) context.getBean("u4"); System.out.println(usert); }
构造器注入方法上面已经说过了,这里看看其他注入
- 依赖注入:Set注入! -- 依赖:bean对象的创建依赖于容器 -- 注入:bean对象中的所有属性,由容器来注入!
看看复杂类型的对象注入
public class Student { private String name; private Address address; private String[] books; private List<String> hobbies; private Map<String,String> card; private Set<String> games; private String wife; private Properties info; } //set,get,toString方法略 public class Address { private String address; } //set,get,toString方法略
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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="student" class="com.person.pojo.Student"> <!--普通注入,value--> <property name="name" value="范进" /> <!--bean注入,ref--> <property name="address" ref="address" /> <!--数组--> <property name="books"> <array> <value>《山海经》</value> <value>《道德经》</value> <value>《周易》</value> </array> </property> <!--list--> <property name="hobbies"> <list> <value>听歌</value> <value>敲代码</value> <value>睡觉</value> </list> </property> <!--Map--> <property name="card"> <map> <entry key="1号" value="许德拉" /> <entry key="2号" value="芬里尔" /> <entry key="3号" value="耶梦加得" /> </map> </property> <!--Set--> <property name="games"> <set> <value>群星</value> <value>dark soul</value> <value>死神</value> </set> </property> <!--null--> <property name="wife"> <null /> </property> <!--Properties--> <property name="info"> <props> <prop key="身份">无名之人</prop> <prop key="饰品">无</prop> <prop key="携带物">粪球</prop> <prop key="衣物">破烂的衣物</prop> </props> </property> </bean> <bean id="address" class="com.person.pojo.Address"> <property name="address" value="无尽虚空"/> </bean> </beans>
测试还是一样
p命名空间约束和c命名空间约束
实体类:
public class User { private String name; private int age; }//set,get,toString方法略
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" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 需要在xml头部加入下面这行 xmlns:p="http://www.springframework.org/schema/p" 后面也是 --> <!--p命名空间注入,可以直接注入属性的值:property--> <bean id="user" class="com.person.pojo.User" p:name="赵无敌" p:age="17" /> <!--c命名空间注入,可以通过构造器注入属性的值:construct-args--> <bean id="user2" class="com.person.pojo.User" c:age="18" c:name="尼德霍格" /> </beans>
测试代码和上面一样
ps:必须要有有参构造器
1.单例模式(Spring默认)——singleton <bean id="user2" class="com.User" c:age="18" c:name="尼德霍格" scope="singleton" /> ps:个人小结,singleton模式下在同一次会话ApplicationContext中通过 context.getBean("user2");创建出来的对象都是一个 2.原型模式——prototype <bean id="user2" class="com.User" c:age="18" c:name="尼德霍格" scope="prototype" /> ps:prototype模式下创建出来的对象都是单独的(新的) 3.其余的request,session,application,这些只能在web开发中使用到
beans.xml
<!--c命名空间注入,可以通过构造器注入属性的值:construct-args--> <bean id="user2" class="com.person.pojo.User" c:age="18" c:name="尼德霍格" scope="prototype" />
测试代码:
@Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Object user = context.getBean("user2"); Object user2 = context.getBean("user2"); System.out.println(user); System.out.println(user==user2); }
输出:
User{name=‘尼德霍格’, age=18}
false
如果去掉beans.xml的scope=“prototype”,则默认为单例模式,输出就是:
User{name=‘尼德霍格’, age=18}
true
- 自动装配是Spring满足bean依赖的一种方式! - Spring会在上下文中中自动寻找,并自动给bean装配属性! - Spring中有三种装配的方式 1.在xml中显示的配置 2.在java中显示配置 3.隐式的自动装配bean【重点】
三个实体类:
public class Cat { public String bark() { return "你是猫"; } @Override public String toString() { return "Cat:" + bark(); } } public class Dog { public String bark(){ return "你是狗"; } @Override public String toString() { return "Dog:"+bark(); } } public class Person { private Cat cat; private Dog dog; private String name; @Override public String toString() { return "Person{" + "cat=" + cat + ", dog=" + dog + ", name='" + name + '\'' + '}'; } public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
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" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <bean id="cat" class="com.person.pojo.Cat" /> <bean id="dog" class="com.person.pojo.Dog" /> <!-- byName:会自动在容器上下文中查找和自己对象后面的值对应的beanId! 例: <bean id="dog" class="com.person.pojo.Dog" /> setDog对应的bean id必须为dog --> <bean id="person" class="com.person.pojo.Person" p:name="头铁的公共" autowire="byName" /> <!--或者 ByType:会自动在容器上下文中查找和自己对象属性类型相同的bean! 例: <bean id="dog22" class="com.person.pojo.Dog" /> setDog对应的bean class必须为Dog <bean id="person" class="com.person.pojo.Person" p:name="头铁的公共" autowire="byType" /> --> </beans>
测试代码
@Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Person person = context.getBean("person", Person.class); System.out.println(person); } /*输出: Person{cat=Cat:你是猫, dog=Dog:你是狗, name='头铁的公共'} 如果 <bean id="cat" class="com.person.pojo.Cat" />改为 <bean id="cat1" class="com.person.pojo.Cat" /> 依旧使用autowire="byName" 那么输出: Person{cat=null, dog=Dog:你是狗, name='头铁的公共'} 但如果改用autowire="byType"则输出还是上面的 */
-- 导入约束:context约束 -- 配置注解的支持: <context:annotation-config/> <?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>
**@Autowired **
-- @Autowired 注解实体类,可以直接在属性上使用即可!也可以在set方式上使用! 使用@Autowired 我们可以不用编写Set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byname! 可见用了@Autowired 后连set方法都可以不用了
实例:
其他不变
Person类:
public class Person { @Autowired private Cat cat; //如果显示定义了 @Autowired的required = false,说明这个对象可以为null,否则不允许为空 //用了@Autowired 后连set方法都可以不用了 @Autowired(required = false) private Dog dog; private String name; @Override public String toString() { return "Person{" + "cat=" + cat + ", dog=" + dog + ", name='" + name + '\'' + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
beans.xml
<!--开启注解支持--> <context:annotation-config/> <bean id="cat" class="com.person.pojo.Cat" /> <!--因为dog注释: @Autowired(required = false) private Dog dog; 所以dog的bean也可以省略,只是输出会是null, 但上面的cat的bean不能省略,不然会报错 --> <bean id="dog" class="com.person.pojo.Dog" /> <bean id="person" class="com.person.pojo.Person" p:name="山沟沟" />
测试输出:
Person{cat=Cat:你是猫, dog=Dog:你是狗, name=‘山沟沟’}
@Nullable 字段标记了这个注解,说明这个字段可以为null;
@Qualifier(value = “?”)
@Autowired @Qualifier(value = "dog22") private Dog dog; private String name; <bean id="dog" class="com.person.pojo.Dog" /> <bean id="dog22" class="com.person.pojo.Dog" />
@Resource注解:java自带的注解
ps:个人理解
@Resource注解无参时,当bean.xml中配置了单个Cat类时,采用的时byType自动装配,多个时采用的时byName自动装配,默认匹配id为cat的bean,否则报错 @Resource注解有参时, @Resource(name = "cat1") ,匹配id为name的值的bean @Resource private Cat cat; <bean id="cat1" class="com.person.pojo.Cat" /> <bean id="cat2" class="com.person.pojo.Cat" />
注解执行装配顺序等说明
@Autowired顺序: byType–》byName–》@Qualifier
@Qualifier没用之前@Autowired标签默认按byType自动装配,@Qualifier用之后按byName自动装配
@Qualifier标签给Dog附上ID:
@Autowired(required = false) @Qualifier(value = "dog22") private Dog dog;
bean Dog配置id和 @Qualifier标签ID一致:<bean id=“dog22” class=“com.person.pojo.Dog” />
实体类:
//等价于 <bean id="user" class="com.person.pojo.User" /> //Component:组件 @Component @Scope("singleton") public class User { //相当于 <property name="name" value="Jerry" /> @Value("Jerry") public String name; }
beans.xml
<!--指定要扫描的包,这个包下的注解就会生效--> <context:component-scan base-package="com.person.pojo" /> <!--开启注解支持--> <context:annotation-config/>
测试代码:
@Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = context.getBean("user", User.class); System.out.println(user.name); } /* 输出: Jerry */
在SpringBoot中随处可见!
实体类:
//这个注解表示这个类被Spring接管了(ps:只有说明/标记的用处,无实际意义) @Component public class User { private String name; @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } public String getName() { return name; } //属性注入值 @Value("羲和") public void setName(String name) { this.name = name; } }
PersonConfig类:
//类似@Component,也会被Spring容器托管,代表一个配置了,相当于beans.xml @Configuration @ComponentScan("com.person.pojo") @Import(PersonConfig2.class)//导入其它配置 public class PersonConfig { //注册一个bean,相当于一个bean标签,这个方法的名字就相当于bean标签中的id属性,这个方法的返回值就相当于bean标签中的class属性 @Bean public User getUser(){ return new User();//就是返回要注入到bean的对象! } }
测试代码:
@Test public void test() { //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig 上下文来获取容器,通过配置类的class对象加载 ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig.class); User user = context.getBean("getUser", User.class); System.out.println(user); } //输出User{name='羲和'}
抽象角色:一般会使用接口或者抽象类来解决 真实角色:被代理的角色 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作 客户:访问代理对象的人!
步骤:
1.接口
2.真实角色
3.代理角色
4.客户访问代理角色
静态代理好处:
- 基于接口—JDK动态代理【暂时使用】
- 基于类:cglib
- java字节码实现:javasisit
需要了解两个类:Proxy(代理),InvocationHandler(调用处理程序)
动态代理好处:
//租房 public interface Rent { public void rent(); } //房东要出租房子 public class Host { public void rent(){ System.out.println("房东要出租房子!"); } }
代理类Proxy:
//代理 public class Proxy implements Rent { private Host host; public Proxy(){ } public Proxy(Host host) { this.host = host; } public void rent() { seeHouse(); host.rent(); contract(); fee(); } public void seeHouse(){ System.out.println("看房"); } public void fee(){ System.out.println("收中介费"); } public void contract(){ System.out.println("签合同"); } }
测试类:
public class Client { public static void main(String[] args) { Host host=new Host(); //代理,中介帮房东租房子,中介会有一些附属操作 Proxy proxy = new Proxy(host); proxy.rent(); } } /* 输出: 看房 房东要出租房子! 签合同 收中介费 */
public interface UserService { void add(); void delete(); void update(); void query(); } //真实对象 public class UserServiceImpl implements UserService{ public void add() { System.out.println("增加了一个用户"); } public void delete() { System.out.println("删除了一个用户"); } public void update() { System.out.println("修改了一个用户"); } public void query() { System.out.println("查询了一个用户"); } }
代理UserServiceProxy类:
public class UserServiceProxy implements UserService{ private UserService userService; public void setUserService(UserService userService) { this.userService = userService; } public void add() { log("add"); userService.add(); } public void delete() { log("delete"); userService.delete(); } public void update() { log("update"); userService.update(); } public void query() { log("query"); userService.query(); } //日志方法 public void log(String msg){ System.out.println("使用了"+msg+"方法"); } }
测试类:
public class Client { public static void main(String[] args) { UserService userService=new UserServiceImpl(); UserServiceProxy proxy=new UserServiceProxy(); proxy.setUserService(userService); proxy.add(); } } /*输出: 使用了add方法 增加了一个用户 */
//租房 public interface Rent { public void rent(); } //房东要出租房子 public class Host implements Rent{ public void rent(){ System.out.println("房东要出租房子!"); } }
代理类ProxyInvocationHandler:
//用这个类,自动生成代理 public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 private Rent rent; public void setRent(Rent rent) { this.rent = rent; } //生成得到的代理类 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this); } //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); //动态代理的本质,就是使用反射机制 Object result = method.invoke(rent, args); fee(); return result; } public void seeHouse(){ System.out.println("看房子"); } public void fee(){ System.out.println("收中介费"); } }
测试类:
public class Client { public static void main(String[] args) { //真实角色 Host host=new Host(); //代理角色 ProxyInvocationHandler pih=new ProxyInvocationHandler(); //通过调用程序处理角色来处理我们要调用的接口对象 pih.setRent(host); Rent proxy = (Rent) pih.getProxy(); //这里的proxy就是动态生成的,我们并没有写 proxy.rent(); } } /* 运行结果: 看房子 房东要出租房子! 收中介费 */
public interface UserService { void add(); void delete(); void update(); void query(); } //真实对象 public class UserServiceImpl implements UserService{ public void add() { System.out.println("增加了一个用户"); } public void delete() { System.out.println("删除了一个用户"); } public void update() { System.out.println("修改了一个用户"); } public void query() { System.out.println("查询了一个用户"); } }
代理类ProxyInvocationHandler:
//用这个类,自动生成代理 public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 private Object target; public void setTarget(Object target) { this.target = target; } //生成得到的代理类 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log(method.getName()); //动态代理的本质,就是使用反射机制 Object result = method.invoke(target, args); return result; } public void log(String msg){ System.out.println("[log]执行了"+msg+"方法"); } }
测试类:
public class Client { public static void main(String[] args) { //真实角色 UserService userService=new UserServiceImpl(); //代理角色 ProxyInvocationHandler pih = new ProxyInvocationHandler(); pih.setTarget(userService);//设置要代理的对象 //动态生成代理类 UserService proxy = (UserService) pih.getProxy(); proxy.add(); proxy.query(); proxy.update(); proxy.delete(); } } ```java public class Client { public static void main(String[] args) { //真实角色 UserService userService=new UserServiceImpl(); //代理角色 ProxyInvocationHandler pih = new ProxyInvocationHandler(); pih.setTarget(userService);//设置要代理的对象 //动态生成代理类 UserService proxy = (UserService) pih.getProxy(); proxy.add(); proxy.query(); proxy.update(); proxy.delete(); } } /* 输出: [log]执行了add方法 增加了一个用户 [log]执行了query方法 查询了一个用户 [log]执行了update方法 修改了一个用户 [log]执行了delete方法 删除了一个用户 */
提供声明式事务;允许用户自定义切面
方式一:使用Spring的API接口【主要是SpringAPI接口实现】
方式二;自定义来实现AOP【主要是切面定义】
方式三:使用注解实现!
public interface UserService { void add(); void delete(); void update(); void select(); } public class UserServiceImpl implements UserService{ public void add() { System.out.println("增加了一个用户"); } public void delete() { System.out.println("删除了一个用户"); } public void update() { System.out.println("修改了一个用户"); } public void select() { System.out.println("查询了一个用户"); } }
测试代码:
@Test public void add() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //动态代理的核心是接口 UserService userService = context.getBean("userService", UserService.class); userService.add(); }
需要横切入的类
public class Log implements MethodBeforeAdvice { /** * * @param method 要执行目标对象的方法 * @param args 参数 * @param target 目标对象 * @throws Throwable */ public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了"+target.getClass().getName()+"的"+method.getName()+"方法"); } } //和 public class AfterLog implements AfterReturningAdvice { /** * * @param returnValue 返回值 * @param method * @param args * @param target * @throws Throwable */ public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了"+method.getName()+"方法 返回结果为:"+returnValue); } }
applicationContext.xml配置
<!--注册bean--> <bean id="userService" class="com.person.service.UserServiceImpl" /> <bean id="log" class="com.person.log.Log" /> <bean id="afterLog" class="com.person.log.AfterLog" /> <!--方式一:使用原生Spring API接口--> <!--配置AOP:需要导入AOP约束--> <aop:config> <!--切入点 expression:表达式 execution(要执行的位置!)--> <aop:pointcut id="pointcut" expression="execution(* com.person.service.UserServiceImpl.*(..))"/> <!--执行环绕增加--> <aop:advisor advice-ref="log" pointcut-ref="pointcut" /> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut" /> </aop:config>
输出:
执行了com.person.service.UserServiceImpl的add方法 增加了一个用户 执行了add方法 返回结果为:null
需要切入的类:
public class DiyPointCut { public void before1(){ System.out.println("=======方法执行前=========="); } public void after1(){ System.out.println("=======方法执行后=========="); } }
applicationContext.xml:
<!--注册bean--> <bean id="userService" class="com.person.service.UserServiceImpl" /> <!--方式二:自定义类--> <bean id="diy" class="com.person.diy.DiyPointCut" /> <aop:config> <!--自定义切面,ref要引用的类--> <aop:aspect ref="diy"> <!--切入点--> <aop:pointcut id="point" expression="execution(* com.person.service.UserServiceImpl.*(..))"/> <!--通知--> <aop:before method="before1" pointcut-ref="point"/> <aop:after method="after1" pointcut-ref="point"/> </aop:aspect> </aop:config>
测试输出:
=======方法执行前========== 增加了一个用户 =======方法执行后==========
切面:
@Aspect//标注这个类是一个切面 public class AnnotationPointCut { @Before("execution(* com.person.service.UserServiceImpl.*(..))") public void before(){ System.out.println("======方法执行前========="); } @After("execution(* com.person.service.UserServiceImpl.*(..))") public void after(){ System.out.println("-------方法执行后----------"); } //在环绕增强中,我们可用给定一个参数,代表我们要获取要处理切入的点 @Around("execution(* com.person.service.UserServiceImpl.*(..))") public void around(ProceedingJoinPoint jp) throws Throwable { System.out.println("环绕前"); Signature signature= jp.getSignature();//获得签名 可忽略 System.out.println("signature:"+signature); //执行方法 Object proceed = jp.proceed(); System.out.println("环绕后"); System.out.println(proceed);//可忽略 } }
applicationContext.xml:
<!--方式三--> <!--注册bean--> <bean id="userService" class="com.person.service.UserServiceImpl" /> <bean id="annotationPointCut" class="com.person.diy.AnnotationPointCut"/> <!-- 开启注解支持 JDK(默认) proxy-target-class="false" cglib proxy-target-class="true" --> <aop:aspectj-autoproxy proxy-target-class="false" />
测试输出:
环绕前 signature:void com.person.service.UserService.add() ======方法执行前========= 增加了一个用户 -------方法执行后---------- 环绕后 null