Spring
1、spring
1.1、优点
spring是一个开源的免费的框架(rongq)
spring是一个轻量级的、非入侵式的框架
控制反转(IOC),面向切面编程(AOP)
支持事务的处理,对框架整合的支持
1.2、spring组成及扩展
2、IOC本质推导
使用maven新建一个项目
添加一个UerDao接口
public interface UserDao {
void getUser();
}
添加一个UserDao实现类
public class UserDaoImpl implements UserDao{
@Override
public void getUser() {
System.out.println("获取用户数据");
}
}
新建一个UserService接口
public interface UserService {
void getUser();
}
添加一个UserService实现类
import com.heng.dao.UserDao;
import com.heng.dao.UserDaoImpl;
//实现UserService接口
public class UserServiceImpl implements UserService {
private UserDao userdao = new UserDaoImpl();
@Override
public void getUser() {
//调用dao层方法
userdao.getUser();
}
}
添加测试类,模拟controller
import com.heng.dao.UserDao;
import com.heng.dao.UserDaoImpl;
//实现UserService接口
public class UserServiceImpl implements UserService {
private UserDao userdao = new UserDaoImpl();
@Override
public void getUser() {
//调用dao层方法
userdao.getUser();
}
}
紧接着我们要去使用MySql的话 , 我们就需要去UserService实现类里面修改对应的实现 :
package com.heng.dao;
public class UserDaoMySqlIpml implements UserDao {
@Override
public void getUser() {
System.out.println("获取MySql用户数据");
}
}
这时又要在测试类写一个测试
private UserDao userDao = new UserDaoMySqlIpml(); // 修改实例对象
@Override
public void getUser() {
userDao.getUser(); // 调用Dao层方法
}
}
如果这样的需求很多,这时候这种设计就会耦合很高,不适合快速开发
方法改进
UserService的实现类不是直接去New一个新的实例,而是留出set接口,让调用者决定选择哪个具体的Dao实现,当然这其中有多态的思想在里面:
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 利用set方法实现
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
接着测试类
public class MyTest {
public static void main(String[] args) {
UserService service = new UserServiceImpl();
((UserServiceImpl) service).setUserDao(new UserDaoMySqlIpml());
service.getUser();
}
// print "获取MySql用户数据"
}
虽然只是一个简单的例子,但其中包含的设计思想确实比较高级的:
- 以前所有东西都是由程序去进行控制创建
- 而现在使用了set注入,主动权交给了调用者
- 程序不用去管怎么创建,而是专注于业务逻辑的实现,耦合性大大降低 . 这也就是IOC的原型 。
IOC本质
**
控制反转(IOC)是一种设计思想,依赖注入(DI)是实现IOC的一种方式。在没有IOC的程序中,我们使用面向对象编程,对象的创建和对象之间的依赖完全硬编码在程序中,对象的创建由程序自己控制,控制反转后对象的创建转移给第三方,也就是“获得依赖对象的方式反转了”。
依赖注入
IOC的另外的名字叫做依赖注入(Dependency Injection),所谓的依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦
3、springhello
1、新建一个maven包,添加UserDao类
package com.heng.dao;
public class UserDao {
private String string;
public UserDao() {
}
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
@Override
public String toString() {
return "UserDao{" +
"string='" + string + '\'' +
'}';
}
}
2、在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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 使用spring创建对象-->
<!--
类型 变量名 = new 类型()
UerDao hello = new UserDap();
id就是hello 然后class就是全路径下的包
name就是实例化后的变量
value就是把spring赋给string这个变量
-->
<bean id="hello" class="com.heng.dao.UserDao">
<property name="string" value="spring"/>
</bean>
</beans>
3、新建测试类
import com.heng.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//写死的这一行
ApplicationContext Context = new ClassPathXmlApplicationContext("beans.xml");
//将id传入
UserDao hello = (UserDao) Context.getBean("hello");
System.out.println(hello.toString());
}
}
4、?思考:
在上面的bean.xml里面,是没有实例化对象的,
那么是谁创建了对象了?
是spring
那对象的属性是怎么设置的?
对象的属性是由spring容器设置的
这个过程就叫控制反转:
控制:谁来和控制对象的创建,传统应用程序的对象是由程序本身控制创建的,例如Hello hello = new Hello();使用spring后,对象是由spring创建的。
反转:程序本身不创建对象,而变成被动的接收对象。
依赖注入:就是利用set方法来进行注入的
IOC是一种编程思想,由主动的编程变成被动的接受。
4、IOC创建对象方式
1、调用无参数构造器创建对象
1.1、User类
package com.heng.pojo;
public class User {
private String name;
public User() {
System.out.println("这是无参构造");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name"+name);
}
}
1.2、beas.xml
<bean id="user" class="com.heng.pojo.User">
</bean>
1.3、Test类
import com.heng.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user= (User) context.getBean("user");
}
}
2、调用有参数构造器创建对象
2.1、User类
在原来的User类上添加一个有参构造方法
public User(String name ){
System.out.println("这是有参构造");
this.name=name;
}
2.2、beans.xml
<!-- 调用有参构造-->
<!-- 方法一 -->
<bean id="user" class="com.heng.pojo.User">
<!--index表示参数下标,从左到右,上面的name就是0,如果有id就是1 -->
<constructor-arg value="呐喊" index="0" type="java.lang.String">
</constructor-arg>
</bean>
<!-- 方法二 -->
<bean id="name" class="java.lang.String">
<constructor-arg value="呐喊"></constructor-arg>
</bean>
<bean id="user" class="com.heng.pojo.User">
<constructor-arg index="0" type="java.lang.String" ref="name">
</constructor-arg>
</bean>
2.3、Test
import com.heng.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user= (User) context.getBean("user");
System.out.println(user.getName());
}
}
总结:在配置文件加载的时候,容器中的管理对象就已经初始化了
5、Spring配置说明
5.1、别名(和MyBatis的别名意思一样)
<!--添加别名就是换个名字,用添加的名字也可以获取对象 -->
<alias name ="user" alias="usernew"
5.2、bean的配置
<!--
bean的唯一标识符:id,和对象一样
class:就是bean的全类型:包名+类型名。
name:就是别名。但是这个name更加好用,可以同时取多个别名
-->
</bean>
<bean id="user" class="com.heng.pojo.User" name="user2,user3">
<property name="name" value="name"/>
</bean>
5.3、import
适用于团队开发,在有多个beans时可以通过import引用,然后汇总到一个ApplicationContenxt.xml上。
<import resource="beans1.xml" />
<import resource="beans2.xml" />
<import resource="beans3.xml" />
6、DI依赖注入环境
6.1、构造器注入
在IOC创建对象时学过了
6.2、Set方法注入【重点】
依赖注入:Set注入
依赖:bean对象的创建依赖于容器中
注入:bean对象的所有属性,由容器注入
Student类
package com.heng.pojo;
import java.util.*;
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobby;
private Map<String,String> card;
private Set<String> games;
private Properties info;
public Student() {
}
public Student(String name, Address address, String[] books, List<String> hobby, Map<String,String> card, Set<String> games, Properties info) {
this.name = name;
this.address = address;
this.books = books;
this.hobby = hobby;
this.card = card;
this.games = games;
this.info = info;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public String[] getBooks() {
return books;
}
public void setBooks(String[] books) {
this.books = books;
}
public List<String> getHobby() {
return hobby;
}
public void setHobby(List<String> hobby) {
this.hobby = hobby;
}
public Map<String,String> getCard() {
return card;
}
public void setCard(Map<String,String> card) {
this.card = card;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", books=" + Arrays.toString(books) +
", hobby=" + hobby +
", card=" + card +
", games=" + games +
", info=" + info +
'}';
}
}
Address类
package com.heng.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address+ '\'' +
'}';
}
}
<?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 id="address" class="com.heng.pojo.Address"/>
<bean id="student" class="com.heng.pojo.Student">
<!-- 第一种,普通值注入 -->
<property name="name" value="呐喊"/>
<!-- bean注入 -->
<property name="address" ref="address"/>
<!-- 数组注入-->
<property name="books">
<array>
<value>java如此简单</value>
<value>Spring</value>
<value>SpringBoot</value>
</array>
</property>
<!-- list -->
<property name="hobby">
<list>
<value>打球</value>
<value>听歌</value>
<value>看电影</value>
</list>
</property>
<property name="card">
<map>
<entry key="身份证" value="12313213654"></entry>
<entry key="学生证" value="1321321465465"></entry>
</map>
</property>
<property name="games">
<set>
<value>LOL</value>
<value>王者荣耀</value>
<value>和平精英</value>
</set>
</property>
<property name="info">
<props>
<prop key="driver">2015</prop>
<prop key="url">男</prop>
<prop key="username">呐喊</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
测试类
import com.heng.pojo.Student;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext Context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) Context.getBean("student");
System.out.println(student.toString());
}
/**
* Student{name='呐喊', address=Address{address='null'},
* books=[java如此简单, Spring, SpringBoot],
* hobby=[打球, 听歌, 看电影],
* card={身份证=12313213654, 学生证=1321321465465},
* games=[LOL, 王者荣耀, 和平精英], i
* nfo={password=123456, driver=2015, url=男, username=呐喊}}
*/
}
7、c命名和p命名空间注入
7.1、p命名
使用这两个命名的时候,需要在头文件上加入以下两个链接
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
编写xml
<bean id="user" class=p"com.heng.dao.User" p:name="呐喊" p:id="123456">
</bean>
测试类
@Test
public void getUser(){
ApplicationContext Context = new ClassPathXmlApplicationContext("beans.xml");
User user = Context.getBean("user",User.class);
System.out.println(ucser.toString());
}
其实和传统的值注入,可以看出,并不需要写太多的代码,用p:代替原来的properties,更加简洁
7.2、c命名
编写xml
<!-- 传统的constructor-arg通过构造方法注入的bean定义-->
<bean id="user1" class="com.heng.dao.User">
<constructor-arg index="0" value="呐喊"/>
<constructor-arg index="1" value="123456"/>
</bean>
<!-- C命名空间通过构造方法注入的bean定义-->
<bean id="user2" class="com.heng.dao.User" c:_0="呐喊" c:_1="1234567"> </bean>
由上面可以知道,和传统的注入代码简洁许多
8、Bean
8.1、自动装配bean
在Spring框架里面是使用set方法和构造方法进行注入的,但是bean对象多了以后这样的注入工作就显得麻烦,还有就是xml文件也会变得很大很杂乱,所以为了简化xml配置文件,提高开发效率我们可以使用autowire
优点:
自动装配可以大大地减少属性和构造器参数的指派。
自动装配也可以在解析对象时更新配置。
在spring中有三种配置方式:
1、在xml中显示的配置
2、在java中显示配置
3、隐式的自动装配
8.2、编写实体类
下面的是byname
byname:会自动在容器的上下文中查找,和自己set方法后面匹配的bean id;
byTyper:会自动在容器的上下文中查找,和自己类型相同的bean id。
Person类
package com.heng.pojo;
public class Person {
private Cat cat;
private Dog dog;
private String 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;
}
@Override
public String toString() {
return "Person{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
Cat
package com.heng.pojo;
public class Cat {
public void shout(){
System.out.println("我是小猫,喵");
}
}
Dog
package com.heng.pojo;
public class Dog {
public void shout(){
System.out.println("我是小狗,汪");
}
}
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 id="cat22" class="com.heng.pojo.Cat"></bean>
<bean id="dog22" class="com.heng.pojo.Dog"></bean>
<!--
byname:会自动在容器的上下文中查找,和自己set方法后面匹配的bean id;
byTyper:会自动在容器的上下文中查找,和自己类型相同的bean id。
-->
<bean id="person" class="com.heng.pojo.Person" autowire="byType">
<property name="name" value="呐喊">
</property>
<!-- 传统的值注入-->
<!-- <property name="dog" ref="dog"></property>-->
<!-- <property name="cat" ref="cat" ></property>-->
</bean>
</beans>
测试类
import com.heng.pojo.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) Context.getBean("person");
System.out.println(person.getName());
person.getDog().shout();
person.getCat().shout();
}
// 呐喊
// 我是小狗,汪
// 我是小猫,喵
}
9、注解实现自动装配
9.1、导入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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--开启注解支持-->
<context:annotation-config/>
<!--只需要配置id和类型即可-->
<bean id="cat" class="com.heng.pojo.Cat"></bean>
<bean id="dog" class="com.heng.pojo.Dog"></bean>
<bean id="person" class="com.heng.pojo.Person"></bean>
</beans>
注意,在导入xml时一定要开启注解支持,要不然是用不了!
9.2、实体类
package com.heng.pojo;
import org.springframework.beans.factory.annotation.Autowired;
public class Person {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
@Autowired
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
@Autowired
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"cat=" + cat +
", dog=" + dog +
", name='" + name + '\'' +
'}';
}
}
另外两个实体类和上面的两个cat和dog一样
import com.heng.pojo.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = (Person) Context.getBean("person");
person.getDog().shout();
person.getCat().shout();
}
// 我是小狗,汪
// 我是小猫,喵
}
@Autowired
直接在属性上使用即可,也可以在set方法上使用。
使用Autowired就可以不用去写set方法了,前面我直接在set方法上用Autowired注解了。前提是你这个自动装配的属性在ioc容器中存在。
9.3、@Autowired(required=false)
如果显示定义了Autowired的required的属性为false,数码这个对象可以为null。否则不允许为空。
@Autowired(required = false)
private Cat cat;
@Autowired
private Dog dog;
private String name;
当配置文件中的id不唯一时,可以通过@resource(name=“id”)来指定唯一id
10、spring注解开发。
导入常用依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
引入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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--指定的包扫描,这个包下的注解才会生效-->
<context:component-scan base-package="com.heng.pojo"/>
<!--开启注解支持-->
<context:annotation-config/>
</beans>
组件
@Conponent 扫描
@Value 赋值
/**
* @Conponent 相当于 <bean id="user" class="com.heng.pojo.User"></bean>
*/
@Component
public class User {
public String name;
@Value("呐喊")
/**
* 相当于<property name="name" value="呐喊"></property>
*/
public void setName(String name) {
this.name = name;
}
}
10.1、使用java Config 进行配置
在这个知识点里面需要了解的是,不需要使用xml进行配置了,都交给java进行配置。
首先还是要先引入头文件
<?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
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--指定的包扫描,这个包下的注解才会生效-->
<context:component-scan base-package="com.heng.pojo"/>
<!--开启注解支持-->
<context:annotation-config/>
</bea
然后编写实体类
package com.heng.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//扫描配置
@Component
public class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
//注入值
@Value("呐喊")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
然后就是配置类
其实原来的xml配置都在这了,只不过更加的方便,由注解进行配置(Spring Boot 就是这样的)
package com.heng.pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//表示这个类为javaconfig 类
//@Configuration 代表的是这是一个配置类。即applicationContect.xml
@Configuration
public class MyConfig {
/**
* 这个bean相当于原来的配置文件中的bean标签
* 这个方法的名相当于原来的id
* 返回的方法名,相当于原来的class
* @return
*/
@Bean
public Person getPerson(){
return new Person();
}
}
测试类
import com.heng.pojo.MyConfig;
import com.heng.pojo.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext Context = new AnnotationConfigApplicationContext(MyConfig.class);
Person getPerson = (Person) Context.getBean("getPerson");
System.out.println(getPerson.getName());
}
}
11、代理模式
代理模式的分类
静态代理
动态代理
11.1、静态代理
静态代理用我的话来来说就是
包租婆–》中介–》租房子的人
中介就是静态代理。包租婆找到中介然后把房子代理给他,就可以不管了,然后租房的人找中介来租房子。
静态代理说白了就是在程序运行前就已经存在代理类的字节码文件,代理类和原始类的关系在运行前就已经确定
定义接口
package com.heng.demo1;
//定义接口
public interface IPerson {
public abstract void sleep();
public abstract void eat();
}
被代理类
package com.heng.demo1;
public class Peson implements IPerson{
@Override
public void sleep() {
System.out.println("zzz```");
}
@Override
public void eat() {
System.out.println("吃饭中");
}
}
package com.heng.demo1;
public class PersonProxy implements IPerson {
private IPerson person;
public PersonProxy(IPerson person) {
this.person = person;
}
@Override
public void sleep() {
person.sleep();
}
@Override
public void eat() {
person.eat();
}
}
测试类
import com.heng.demo1.IPerson;
import com.heng.demo1.PersonProxy;
import com.heng.demo1.Peson;
public class MyTest {
public static void main(String[] args) {
IPerson proxy = new PersonProxy(new Peson());
proxy.sleep();
proxy.eat();
}
}
静态代理,后期维护代价太大,更加倾向于动态代理
11.2、动态代理
定义接口
package com.heng.deno03;
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
实现接口
package com.heng.deno03;
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("更新了一个用户");
}
@Override
public void query() {
System.out.println("查找了一个用户");
}
}
动态主要机制是通过反射机制来实现的。实现InvocationHandler接口。
其实动态代理的主要代码是可以写死的。
和工具类一样
package com.heng.deno03;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyHandler 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);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target,args);
return result;
}
}
测试
package com.heng.deno03;
import com.heng.demo2.UserDao;
public class client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userDao = new UserServiceImpl();
//代理角色,不存在
ProxyHandler pih= new ProxyHandler();
pih.setTarget(userDao);//设置要代理的对象
//动态生成代理对象
UserService proxy =(UserService) pih.getProxy();
//调用实例
proxy.add();
}
}
12、AOP
1、AOP的基本概念
AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Tc7VFIc-1598706029470)(C:\Users\12920\AppData\Roaming\Typora\typora-user-images\image-20200829132123039.png)]
Aspect
(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。Joint point
(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。Pointcut
(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。Advice
(增强):Advice 定义了在Pointcut
里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。Target
(目标对象):织入Advice
的目标对象.。Weaving
(织入):将Aspect
和其他对象连接起来, 并创建Advice
d object 的过程
了解完AOP之后,我们来看下怎么实现AOP吧
2、实现AOP(第一种)
导入依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
新建一个接口和接口的实现类
package com.heng.service;
public interface UserService {
void add();
void delete();
void update();
void select();
}
package com.heng.service;
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("更新了一个用户");
}
@Override
public void select() {
System.out.println("选择了一个用户");
}
}
接着我们要实现的是前置日志和后置日志
其实就是起到一个记录的作用,方便自己记录
package com.heng.Log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BeforeLog implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
package com.heng.Log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法的返回结果"+returnValue);
}
}
接下来的就是最重点的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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean-->
<bean id="userService" class="com.heng.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.heng.Log.BeforeLog"/>
<bean id="afterLog" class="com.heng.Log.AfterLog"/>
<!-- 配置aop 导入aop的约束-->
<aop:config>
<!-- 切入点 :execution(执行的位置)-->
<aop:pointcut id="pointcut" expression="execution(* com.heng.service.UserServiceImpl.*(..))"/>
<!-- 执行环绕-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试类
import com.heng.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 动态代理是面向接口
UserService userService = (UserService) Context.getBean("userService");
userService.add();
}
}
3、实现AOP(第二种)
自定义类来实现
package com.heng.diy;
public class DiyPointcut {
public void before(){
System.out.println("----执行前---");
}
public void after(){
System.out.println("---执行后---");
}
}
然后配置
<!-- 方法二 自定义类-->
<bean id="diy" class="com.heng.diy.DiyPointcut"/>
<aop:config>
<!-- 自定义切面 ref:要引入的类-->
<aop:aspect ref="diy">
<!-- 切入点-->
<aop:pointcut id="ponit" expression="execution(* com.heng.diy.DiyPointcut.*(..))"/>
<!-- 通知-->
<aop:before method="before" pointcut-ref="ponit"/>
<aop:after method="after" pointcut-ref="ponit"/>
</aop:aspect>
</aop:config>
总结
其实和第一种方法实现的道理是一样的,只不过没有实现spring的API来实现而已,这个是自己定义了一个类,然后在配置那里,注册进去,然后通过切入点去到通知层面调用这个方法。
4、注解实现AOP
配置AOP
<!-- 开启注解支持-->
<aop:aspectj-autoproxy/>
<bean id="annotation" class="com.heng.aono.Annotation"/
package com.heng.aono;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//标注这个类是一个切面类
//@Component
@Aspect
public class Annotation {
@Before("execution(* com.heng.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("----方法执行前---");
}
@After("execution(* com.heng.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---方法执行后---");
}
}
13、Mybatis整合
1、实现方式一
1.1、编写数据库源
<!--数据源,其实就是使用spring的数据源来替换原来的mybatis连接数据库的配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
注意:
在这里的数据源其实就是我们在学mybatis时配置的数据源,直接可以拿来使用。
1.2、编写接口和实现类
接口类
package com.heng.dao;
import com.heng.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> getUser();
}
实现类
package com.heng.dao;
import com.heng.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper{
//在原来我们所有的操作都用sqlSessionFactory实现,现在我们使用.SqlSessionTemplate
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> getUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.getUser();
}
}
在spring中注册
<bean id="userMapper" class="com.heng.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
还有一个UserMapper.xml不要忘记!!
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heng.dao.UserMapper">
<select id="getUser" resultType="User">
select * from mybatis.user;
</select>
</mapper>
当然了,还有实体类(其实如果想偷懒的话可以导入lombot插件,用注解可以实现下面的代码)
package com.heng.pojo;
public class User {
private int id;
private String name;
private String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
1.3、SqlSessionFactory SqlSession
在原来的mybatis时,我们是需要一个工具类来声明 SqlSessionFactory 和 SqlSession
但是在整合时直接就可以整合到spring的xml中。
package com.heng.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class Mybatis {
public static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "Mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = (new SqlSessionFactoryBuilder()).build(inputStream);
} catch (IOException var3) {
var3.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
整合时
<!--sqlSessFactory,原来的要在一个工具类声明这个sqlSessFactory,现在直接在这里加入即可。-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 绑定mybatis配置文件 -->
<property name="configLocation" value="classpath:Mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/heng/dao/UserMapper.xml"/>
</bean>
<!--SqlSessionTemplate就是我们在mybatis使用的SqlSession,在这里注入SqlSessionTemplate就可以不用newSqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 在这里的话,如果由set方法就不用ref注入了 -->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
1.6、测试
import com.heng.dao.UserMapper;
import com.heng.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
public class MyTest {
public static void main(String[] args) throws IOException {
ApplicationContext Context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = (UserMapper) Context.getBean("userMapper");
for (User user : userMapper.getUser()) {
System.out.println(user);
}
}
}
附:
完整applicationContext
<?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">
<!--数据源,其实就是使用spring的数据源来替换原来的mybatis连接数据库的配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<!--sqlSessFactory,原来的要在一个工具类声明这个sqlSessFactory,现在直接在这里加入即可。-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 绑定mybatis配置文件 -->
<property name="configLocation" value="classpath:Mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/heng/dao/UserMapper.xml"/>
</bean>
<!--SqlSessionTemplate就是我们在mybatis使用的SqlSession,在这里注入SqlSessionTemplate就可以不用newSqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 在这里的话,如果由set方法就不用ref注入了 -->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.heng.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
er()) {
System.out.println(user);
}
}
}
**附:**
完整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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--数据源,其实就是使用spring的数据源来替换原来的mybatis连接数据库的配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
<!--sqlSessFactory,原来的要在一个工具类声明这个sqlSessFactory,现在直接在这里加入即可。-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 绑定mybatis配置文件 -->
<property name="configLocation" value="classpath:Mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/heng/dao/UserMapper.xml"/>
</bean>
<!--SqlSessionTemplate就是我们在mybatis使用的SqlSession,在这里注入SqlSessionTemplate就可以不用newSqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 在这里的话,如果由set方法就不用ref注入了 -->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.heng.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
</beans>
其实还有一个整合方式,感兴趣可以去官方文档看一下,比上面的简单,但是方法只要懂一个即可,其它的就可以旁类触痛了。