- UserDao 接口
package dao;
public interface UserDao {
void getUser();
}
- USerOracleDaoImpl实现类
package dao;
public class USerOracleDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("用户的信息获取");
}
}
- UsersqlDaoImpl实现类
package dao;
public class UserSqImpl implements UserDao {
@Override
public void getUser() {
System.out.println("Mysql用户的信息的获取");
}
}
service业务层
- UserService接口
package service;
public interface UserService {
void getUser();
}
- UserServiceImpl实现类
package service.impl;
import dao.UserDao;
import dao.UserDaoImpl;
import service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao=new USerOracleDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
- 结构图
对于上述中出现的三层中如果我们在dao层中添加一个lmpl实现类,则对于我们的服务层中的UserServiceImpl怎需要重新的进行修改,这样的代码实际上是非常不友好的
- 为了降低这种的耦合度需要对器其代码 进行如下的修改
package service.impl;
import dao.UserDao;
import service.UserService;
public class UserServiceImpl implements UserService {
UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao=userDao;
}
// private UserDao userDao=new USerOracleDaoImpl();
@Override
public void getUser() {
userDao.getUser();
}
}
- 此处我只关心是实现类的调用者(客户端)传入什么样的dao层对象即可
import dao.UserSqlDaoImpl;
import org.junit.Test;
import service.UserService;
import service.impl.UserServiceImpl;
public class MyTest {
@Test
public void test(){
UserService userService =new UserServiceImpl(new UserSqlDaoImpl());
userService.getUser();
}
}
IOC控制反转的思想
控制反转可以大大的降低程序直接的耦合性,不用去管理对象的创建
HelloSpring 通过set/get方式注入到容器 构建一个实体测试类public class User {
private String name;
private String site;
private Integer age;
public User() {
}
public User(String name, String site, Integer age) {
this.name = name;
this.site = site;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSite() {
return site;
}
public void setSite(String site) {
this.site = site;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + ''' +
", site='" + site + ''' +
", age=" + age +
'}';
}
}
Spring的容器配置
- daos.xml
构造测试类
import com.kuang.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test(){
ApplicationContext ac;
ac= new ClassPathXmlApplicationContext("daos.xml");
User user = (User) ac.getBean("user");
System.out.println(user);
}
}
通过构造方法的方式注入 创建一个只提供有参构造的Studet实体类输出结果
com.kuang.pojo.User{name=‘kakarote’, site=‘地球’, age=18}
Process finished with exit code 0
package com.kuang.pojo;
public class Student {
private String name;
private Integer id;
private String size;
public Student(String name, Integer id, String size) {
this.name = name;
this.id = id;
this.size = size;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", id=" + id +
", size='" + size + ''' +
'}';
}
}
创建一个applicationContext.xml的配置文件
- 通过构造方法的方式注入到我们的Spring容器当中
构造测试方法
@Test
public void tese2(){
ApplicationContext ac;
ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student= (Student) ac.getBean("student");
System.out.println(student.toString());
}
输出的结果
Student{name=‘kakarote’, id=9527, size=‘地球’}
Process finished with exit code 0
-
通过构造方法的参数索引下标注入值
-
不指定名称和下标索引的注入
- 注意:此种方式的注入一定要按类中构造方法的参数的顺序来进行注入。
对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属性)。根据自动注入判断标准的不同,可以分为两种:
- byName:根据名称自动注入
- byType: 根据类型自动注入
- Stu类
package com.kuang.pojo;
public class Stu {
private String schoolname;
public String getSchoolname() {
return schoolname;
}
public void setSchoolname(String schoolname) {
this.schoolname = schoolname;
}
@Override
public String toString() {
return "stu{" +
"schoolname='" + schoolname + ''' +
'}';
}
}
- Student类
package com.kuang.pojo;
public class Student {
private String name;
private Integer id;
private String size;
private Stu stu;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public Stu getStu() {
return stu;
}
public void setStu(Stu stu) {
this.stu = stu;
}
public Student() {
}
public Student(String name, Integer id, String size) {
this.name = name;
this.id = id;
this.size = size;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + ''' +
", id=" + id +
", size='" + size + ''' +
", student=" + stu +
'}';
}
}
添加注入
添加测试类
@Test
public void test2(){
ApplicationContext ac;
ac=new ClassPathXmlApplicationContext("daos.xml");
Student stu= (Student) ac.getBean("mystudent");
System.out.println(stu.toString());
}
输出结果
Student{name=‘jijibh’, id=8760, size=‘地球’, student=stu{schoolname=‘卡卡罗特学校’}}
Process finished with exit code
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YwfHCcth-1654848301133)(C:UsersPeggyAppDataRoamingTyporatypora-user-imagesimage-20220609203308319.png)]
(2) byType 方式自动注入- 使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类, 要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QEupt7Jo-1654848301133)(C:UsersPeggyAppDataRoamingTyporatypora-user-imagesimage-20220609203457813.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-INevaCKB-1654848301134)(C:UsersPeggyAppDataRoamingTyporatypora-user-imagesimage-20220609203509626.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HL70d8c9-1654848301134)(C:UsersPeggyAppDataRoamingTyporatypora-user-imagesimage-20220609203530430.png)]
关于注解的spring容器的注入-
创建测试实体类
- School类
package com.kuang; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component("MySchool") public class School { @Value("龙珠超人学院") private String name; @Value("超魔幻现实") private String site; @Override public String toString() { return "School{" + "name='" + name + ''' + ", site='" + site + ''' + '}'; } }- Stu实体类
package com.kuang; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component("myStu") public class Stu { @Value("卡卡罗特") private String name; @Value("18") private Integer age; @Autowired private School school; @Override public String toString() { return "Stu{" + "name='" + name + ''' + ", age=" + age + ", school=" + school + '}'; } } -
创建spring注入配置文件
- applicationContext.xml
-
构建测试类
- MyTest
import com.kuang.Stu; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test(){ String congfigLocation="applicationContext.xml"; ApplicationContext ac=new ClassPathXmlApplicationContext(congfigLocation); Stu stu = (Stu) ac.getBean("myStu"); System.out.println(stu); } }常用的注解 1)创建对象的注解-
@Component :创建所有对象都可以使用此注解,除了控制器,业务逻辑层,数据访问层的对象
-
@Controller:创建控制器层的对象,此对象可以接收用户请求,返回处理结果
-
@Service:创建业务逻辑层的对象,此对象可施事务控制,向上给控制器返回数据,向下调用数据访问层
-
@Repository:创建数据访问层的对象 ,对数据库中的数据进行增删改查操作
-
@Value:给简单类型赋值
-
@Autowired:给引用类型按类型注入
-
在·@Qualifier:给引用类型按名称注入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SZGgLMqf-1654848301135)(C:UsersPeggyAppDataRoamingTyporatypora-user-imagesimage-20220609212939250.png)]
- UserMapper
package repository;
import jopa.User;
public interface UsersMapper {
int insert(User user);
}
-
impl
- UserMapperImpl
package repository.Impl; import jopa.User; import org.springframework.stereotype.Repository; import repository.UsersMapper; @Repository public class UsersMapperImpl implements UsersMapper { @Override public int insert(User user) { System.out.println("用户:"+user+"添加成功"); return 1; } }
-
UserService
package service; import jopa.User; public interface UserService { int insert(User user); } -
impl
- UserServiceImpl
package service.Impl; import jopa.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import repository.UsersMapper; import service.UserService; @Service public class UserServiceImpl implements UserService { @Autowired UsersMapper usersMapper; @Override public int insert(User user) { return usersMapper.insert(user); } }
- UserController
package controller;
import jopa.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import service.UserService;
@Controller
public class UserController {
@Autowired
UserService userService;
public int inset(User user){
return userService.insert(user);
}
}
关于MVC三层架构的注解
- @repository
- @service
- @controller
注解与 XML 的对比上述三个注解其实作用都是一样的唯一的区别只是注解的名称不同而已
这三个的注解的作用只是将这三个类注入到我们spring容器当中,与@Comoenter的尊用是一样的
只不过是@Comoenter的衍生的扩展注解
注解优点是:
-
方便
-
直观
-
高效(代码少,没有配置文件的书写那么复杂)。
*** 其弊端也显而易见:***
- 以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。
XML 方式优点是:
-
配置和代码是分离的
-
在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
*** xml 的缺点是:***
- 编写麻烦,效率低,大型项目过于复杂。
拆分策略在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将
Spring 配置文件分解成多个配置文件。
- 常见的拆分策略有按模块拆分和按层拆分,当然在实际工作中,会有更细的拆分方法。
-
按模块拆分,例如用户模块applicationContext_user.xml,applicationContext_book.xml
每个xml文件中都包含相应的xxxController,xxxService,xxxDao的对象的创建。
-
按层拆分,例如拆分成applicationContext_controller.xml, applicationContext_service.xml,
applicationContext_dao.xml等,每个xml文件中有相关对象的创建,
例如:applicationContext_controller.xml文件中包含userController,bookController等对象的创建。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wv2LdBEo-1654848301135)(C:UsersPeggyAppDataRoamingTyporatypora-user-imagesimage-20220609214436601.png)]
拆分后整合-
可以使用通配符 ***** 进行整合
但此时要求父配置文件名不能满足* 所能匹配的格式,否则将出现循环递归包含。
就本例而言,父配置文件不能匹配 applicationContext-*.xml 的格式,即不能起名为applicationContext-total.xml。
-
多个配置文件中有一个总文件,总配置文件将各其它子文件通过 < import />引入。在 Java
代码中只需要使用总配置文件对容器进行初始化即可。
注意:可以使用通配符 * 进行批量整合。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rzbf9u2z-1654848301136)(C:UsersPeggyAppDataRoamingTyporatypora-user-imagesimage-20220609214748583.png)]
- 利用java类代替之前xml配置文件进行开发
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yMAPcstF-1654848301140)(C:UsersPeggyAppDataRoamingTyporatypora-user-imagesimage-20220610094933886.png)]
-
静态代理
角色分析:
- 抽象角色:一般会使用抽象接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色之后我们一般会做一些附属的操作
- 客户:访问代理反正对象的人
package com.kuang.rent; public interface Rent { //生成一个出租房屋的接口 void rent(); }生成一个实现类package com.kuang.rent; public class Host implements Rent{ //房东实现房屋出租的接口 @Override public void rent() { System.out.println("房东出租房屋"); } }创建一个代理类package com.kuang.proxy; import com.kuang.rent.Host; import com.kuang.rent.Rent; public class Proxy { private Host host; public Proxy(Host host) { this.host = host; } public void rent(){ host.rent(); } //代理的功能扩展 public void see(){ System.out.println("中介带领我们进行了房屋的参观"); } }创建一个测试类import com.kuang.proxy.Proxy; import com.kuang.rent.Host; import org.junit.Test; public class Mytest { @Test public void test(){ Proxy proxy=new Proxy(new Host()); proxy.rent(); proxy.see(); } }
输出结果
房东出租房屋
中介带领我们进行了房屋的参观Process finished with exit code 0
结果分析
- 我们的代理类代理调用了房屋出租的接口,并调用接口的方法,从而实现了房东类中方法的调用,同时通过代理类对其功能进行了扩展,比如添加了see()方法,但是同样的从存在一个比较严重问题。
- 当我们扩展接口中的功能的时候,我们的代理类需要进行代码的大量修改
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WAbxFIzw-1654848301141)(C:UsersPeggyAppDataRoamingTyporatypora-user-imagesimage-20220610101700077.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBnN67u6-1654848301141)(C:UsersPeggyAppDataRoamingTyporatypora-user-imagesimage-20220610101716307.png)]
**优点:**真实角色也就是业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。(只需要进行代码的扩展,不需要在原来的代码上进行修改)
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度
动态代理
- 动态代理和静态代理的角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口— JDK代理【我们在这里时使用】
- 基于类: cglib
- java字节码的实现:javasist
- 需要了解的两个类: proxy 代理 ,invocationHandler
在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
-
ProxyInvcationHandler
package com.kuang.mode2.proxy; import org.springframework.cglib.proxy.Proxy; import org.springframework.cglib.proxy.InvocationHandler; import java.lang.reflect.Method; public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 private Object rent; public void setRent(Object rent){ this.rent=rent; } //生成的代理类 public Object getRent(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this); } public static void log(String message){ System.out.println("执行了"+message+"方法"); } public static void end(){ System.out.println("中介看房结束"); } //处理代理实例并返回代理的结果 //在执行此方法的同时就会通过反射生成一个代理的对象 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log("log()"); Object result = method.invoke(rent,args); end(); return result; } } -
测试类
import com.kuang.mode2.rent.Host;
import com.kuang.mode2.rent.Rent;
import com.kuang.mode2.proxy.ProxyInvocationHandler;
import org.junit.Test;
public class Mytest {
@Test
public void test(){
//真实的角色
Host host=new Host();
//代理的角色现在没有
ProxyInvocationHandler pip=new ProxyInvocationHandler();
//通过调用程序处理的角色来处理我们调用的接口对象
pip.setRent(host);
Rent proxy= (Rent) pip.getRent();
proxy.rent();
}
}
输出结果
执行了log()方法
房东出租房屋
中介看房结束Process finished with exit code 0
总结:通过InvocationHandler反射生成了一个代理类,并在类加载的同时就可以将,代理的扩展增强功能添加到代理对象当中,直接执行代理对象的方法即可。
AOP 面向切面编程 AOP概述AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。
代码实例AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB的动态代理。
方式一: 使用Spring的API接口【主要是SpringAPI接口实现】
- pom.xml配置文件
org.aspectj
aspectjweaver
1.9.5
-
log
-
Log前置对象
package com.kuang.log; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.stereotype.Component; import java.lang.reflect.Method; //Log前置增强类 @Component("log") public class Log implements MethodBeforeAdvice { //mathod:要执行的目标对象的方法 //args:参数 //target:目标对象 @Override public void before(Method method, Object[] objects, Object target) throws Throwable { System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行"); } } -
AfterLog 后置对象
package com.kuang.log; import org.springframework.aop.AfterReturningAdvice; import org.springframework.stereotype.Component; import java.lang.reflect.Method; //后置增强类 @Component("afterLog") public class AfterLog implements AfterReturningAdvice { @Override public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable { System.out.println("执行了"+method.getName()+"方法,其返回值的结果为:"+returnValue); } } -
-
service
-
UserService 服务类接口
package com.kuang.service; public interface UserService { void delete(); void install(); void updata(); } -
impl
-
UserServiceImpl服务实现类
package com.kuang.service.impl; import com.kuang.service.UserService; //@Component("userService") public class UserServicelmpl implements UserService { @Override public void delete() { System.out.println("删除一个用户"); } @Override public void install() { System.out.println("添加一个用户"); } @Override public void updata() { System.out.println("更新一个用户"); } }
-
-
-
测试类
-
MyTest
import com.kuang.service.UserService; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void mytest(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //动态代理是接口 UserService userService = (UserService) context.getBean("userService"); userService.delete(); userService.install(); } }
-
-
spring容器的配置文件
-
applicationContext.xml
-
-
输出结果
-
com.kuang.service.impl.UserServicelmpl的delete被执行
删除一个用户
执行了delete方法,其返回值的结果为:null
com.kuang.service.impl.UserServicelmpl的install被执行
添加一个用户
执行了install方法,其返回值的结果为:nullProcess finished with exit code 0
-
方式二: 自定义类来实现AOP【主要是切面定义】
-
创建一个diy的自定义类
-
diy
-
DiyPointCut
package com.kuang.diy; public class DiyPointCut { public void before(){ System.out.println("方法执行之前"); } public void after(){ System.out.println("方法执行之后"); } }
-
-
修改spring的xml配置文件
-
applicaionContext.xml
-
-
测试类
-
MyTest
import com.kuang.service.UserService; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { @Test public void test(){ ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = (UserService) context.getBean("userService"); userService.install(); } }
执行结果
方法执行之前
添加一个用户
方法执行之后Process finished with exit code 0
方式三: 使用注解实现!
-
在diy包下定义注解实现的AnnotationPointCut增强类
-
diy
-
AnnotationPointCut
//声明式事务! @Aspect //标注这个类是一个切面 public class AnnotationPointCut { @Before("execution(* com.kuang.service.UserServiceImpl.*(..))") public void before(){ System.out.println("====方法执行前===="); } @After("execution(* com.kuang.service.UserServiceImpl.*(..))") public void after(){ System.out.println("====方法执行后===="); } //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点; @Around("execution(* com.kuang.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); } }
-
-
在Spring配置文件中,注册bean,并增加支持注解的配置。
-
applicationContext.xml
-
测试类测试



