Spring
简介
Spring : 春天 --->给软件行业带来了春天
2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。
2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。
很难想象Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术
优点
1、Spring是一个开源免费的框架 , 容器 .
2、Spring是一个轻量级的框架 , 非侵入式的 .
3、控制反转 IoC , 面向切面 Aop
4、对事物的支持 , 对框架的支持
1.ioc
导包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.0.RELEASE</version> </dependency>
1.UserDao接口
public interface UserDao { void getUser(); }
2.UserDaoImpl实现类
public class UserDaoImpl implements UserDao { public void getUser() { System.out.println(11111); } }
3.UserService业务接口
public interface UserService { void getUser(); }
4.UserServiceImpl业务实现类
public class UserServiceImpl implements UserService { private UserDao userDao=new UserDaoImpl(); public void getUser() { userDao.getUser(); } }
5.测试
public static void main(String[] args) { UserService userService=new UserServiceImpl(); userService.getUser(); }
6.在业务层实现类中添加set接口
private UserDao userDao; //利用set实现动态实现值的注入! public void setUserDao(UserDao userDao) { this.userDao = userDao; }
-
之前,程序是主动创建对象!
-
现在使用了set注入后,程序不在有主动权,而变成了被动接受对象!
IOC本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
3.helloSpring
ioc创建对象的方式
1.使用无参构造创建对象!
<bean id="user" class="com.mh.pojo.User"> <property name="name" value="fyh"/> </bean>
2.有参构造 创建对象
1.下标赋值
<bean id="user" class="com.mh.pojo.User"> <constructor-arg index="0" value="fyh"/> </bean>
2.类型
<bean id="user" class="com.mh.pojo.User"> <constructor-arg type="java.lang.String" value="fyh"/> </bean>
3.直接通过参数名
<bean id="user" class="com.mh.pojo.User"> <constructor-arg name="name" value="fyh"/> </bean>
4.Spring配置
1.别名
如果添加了别名,我们也可以使用别名获取到这个对象
<alias name="user" alias="namenew"/>
2.Bean的配置
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello"> <property name="name" value="Spring"/> </bean>
3.import
团队的合作通过import来实现 .
<import resource="{path}/beans.xml"/>
5.依赖注入
1.构造器注入
在上面
2.set方式注入
依赖注入:set注入
依赖:bean对象的创建依赖于容器
注入:bean对象中的所有属性,由容器来注入
环境创建
1.复杂类型
public class Address { private String address;
2.真是测试对象
public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String,String> card; private Set<String> games; private String wife; private Properties info;
3.applicationContext.xml
<bean id="student" class="com.mh.pojo.Student"> <!--第一种 普通注入--> <property name="name" value="fyh"/> <bean>
4.测试类
public static void main(String[] args) { ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); Student student=(Student) context.getBean("student"); System.out.println( student.getAddress()); }
完善注入信息
<bean id="address" class="com.mh.pojo.Address"/> <bean id="student" class="com.mh.pojo.Student"> <!--第一种 普通注入--> <property name="name" value="fyh"/> <!--第二种 Bean注入--> <property name="address" ref="address"/> <!--第三种 数组注入--> <property name="books"> <array> <value>1</value> <value>2</value> <value>3</value> </array> <!--list注入--> </property> <property name="hobbys"> <list> <value>1</value> <value>2</value> <value>3</value> </list> </property> <!--map注入--> <property name="card"> <map> <entry key="身份证" value="。。。。。"/> <entry key="银行卡" value="1111111"/> </map> </property> <!--set注入--> <property name="games"> <set> <value>1</value> <value>2</value> <value>3</value> </set> </property> <!--null--> <property name="wife"> <null></null> </property> <!--Properties注入--> <property name="info"> <props> <prop key="学号">22222222</prop> <prop key="学号">22222222</prop> </props> </property> </bean>
3.拓展方式注入
可以用c命名空间和p命名空间注入
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--p命名空间注入--> <bean id="user" class="com.mh.pojo.User" p:name="fyh" p:age="10"/> <bean id="user2" class="com.mh.pojo.User" c:age="10" c:name="fyh"/> </beans>
4.Bean的作用域
1.单例模式(spring的默认机制)
<bean id="user2" class="com.mh.pojo.User" c:age="10" c:name="fyh" scope="singleton"/>
2.原型模式:每次从容器中get的时候,都会产生一个新对象!
<bean id="user2" class="com.mh.pojo.User" c:age="10" c:name="fyh" scope="prototype"/>
3.request,session,application这些只能在web开发中使用到!
6.bean的自动装配
自动装配是spring满足bean依赖的一种方式!
spring会在上下文中自动寻找并自动给bean装配属性!
在spring中有三种装配方式
1.在xml中显示的配置
2.在java中显示配置
3.隐式的自动装配bean(重要的)
1.测试
环境搭建
一个人有两个宠物!
bean id="cat" class="com.mh.pojo.Cat"/> <bean id="dog" class="com.mh.pojo.Dog"/> <bean id="people" class="com.mh.pojo.People"> <property name="name" value="fyh"/> <property name="dog" ref="dog"/> <property name="cat" ref="cat"/> </bean>
public void test1(){ ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); People people=context.getBean("people",People.class); people.getCat().shout(); people.getDog().shout();
byname:会自动在容器上下文查找,和自己对象set方法后面的值对应的beanid!
<bean id="cat" class="com.mh.pojo.Cat"/> <bean id="dog" class="com.mh.pojo.Dog"/> <bean id="people" class="com.mh.pojo.People" autowire="byName"> <property name="name" value="fyh"/> </bean>
bytype:会自动在容器上下文查找,和自己对象容器相同的bean
<bean class="com.mh.pojo.Cat"/> <bean class="com.mh.pojo.Dog"/> <bean id="people" class="com.mh.pojo.People" autowire="byType"> <property name="name" value="fyh"/> </bean>
注解实现自动装配
使用注解须知:
1.导入约束,context约束
2.配置注解的支持
<?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 可以不用set方法
直接在属性上使用即可
@nullable 字段标记了这个注解,说明这个字段可以为null
public @interface Autowired { boolean required() default true; }//如果定义required属性为false。说明这个对象可以为null,否则不允许为空
测试代码
public class People { @Autowired(required = false) private Cat cat; @Autowired private Dog dog; private String name;
如果@Autowired 自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired 完成的时候,我们可以使用@Qualifier(value="xxx")去配置@Autowired 的使用,指定一个唯一的bean对象注入!
public class People { @Autowired @Qualifier(value="cat11") private Cat cat; @Autowired @Qualifier(value="dog22222") private Dog dog; private String name;
@Resource注解
public class People { @Resource(name="cat2") private Cat cat; @Resource(name="dog22222") private Dog dog;
@Resource和@Autowired的区别:
都是用来自动装配的,都可以放在属性字段上
@Autowired通过byname的方式实现
@Resource默认通过byname的方式实现,如果找不到名字,则通过bytype实现!如果俩个都找不到的情况下就报错。
执行顺序不同:@Autowired 通过bytype的方式实现,@Resource默认通过byname的方式实现,
7.使用注解开发
在spring4之后,要使用注解开发,必须保证aop的包导入了。
使用注解需要导入context约束,增加注解的支持。
<?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>
1.bean
1、配置扫描哪些包下的注解
<!--指定注解扫描包--> <context:component-scan base-package="com.kuang.pojo"/>
2、在指定包下编写类,增加注解
@Component("user") // 相当于配置文件中 <bean id="user" class="当前注解的类"/> public class User { public String name = "fyh"; }
如何注入
@Component("user") // 相当于配置文件中 <bean id="user" class="当前注解的类"/> public class User { @Value("fyh") // 相当于配置文件中 <property name="name" value="fyh"/> public String name; }
3.衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层。
-
dao【@Repository】
-
service【@Service】
-
controller【@Controller】
这四个注解都是一样的,都是将某个类注册到Spring容器中,装配bean。
4.自动装配置
@Autowired:自动装配通过类型,名字 如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx") @Nullable:字段标记了这个注解,说明这个字段可以为Null @Resource:自动装配通过名字,类型。
5.作用域
@Scope("prototype") 可以设置单例和原型
8.使用Java的方式配置spring
完全不使用spring的xml配置,全权交给Java来做!
JavaConfig是spring的一个子项目,在spring4之后,它成为了一个核心功能!
config
import com.mh.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; //这个也会spring容器托管,注册到容器中,因为它本来就是一个@Component //@Configuration代表一个配置类,和applicationContext.xml一样 @Configuration @ComponentScan("com.mh.pojo") public class fyhConfig { //注册一个bean,就相当于我们之前写的一个bean标签 //这个方法的名字相当于bean标签的id属性 //这个方法的返回值相当于bean标签中的class属性 @Bean public User getUser(){ return new User();//就是返回要注入到bean的对象 } }
测试
@Test public void test3(){ //如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig 上下文来获取容器,通过配置类的class对象加载 ApplicationContext context=new AnnotationConfigApplicationContext(fyhConfig.class); User user=(User) context.getBean("getUser"); System.out.println(user.getName()); }
9.代理模式
代理模式就是aop的底层【springaop和springmvc】
代理模式的分类:
-
静态代理
-
动态代理
1.静态代理
角色分析:
-
抽象角色:一般会使用接口和抽象类解决
-
真实角色:被代理的角色
-
代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作
-
客户:访问代理对象的人
代码:
1.接口
public interface Rent { public void rent(); }
2.真实角色
public class Host implements Rent { public void rent() { System.out.println("房东要出租房子"); } }
3.代理角色
public class Proxy implements Rent{ private Host host; public Proxy(){ } public Proxy(Host host) { this.host = host; } public void rent() { host.rent(); seehouse(); hetong(); fare(); } public void seehouse(){ System.out.println("中介带你看房"); } public void hetong(){ System.out.println("签合同"); } public void fare(){ System.out.println("中介费"); } }
4.客户端访问代理角色
public class Client { public static void main(String[] args) { Host host=new Host(); //代理 Proxy proxy=new Proxy(host); proxy.rent(); } }
静态代理的好处:
-
可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
-
公共的业务由代理来完成 . 实现了业务的分工 ,
-
公共业务发生扩展时变得更加集中和方便 .
缺点 :
-
类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
2.动态代理
动态代理的角色和静态代理的一样 .
动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
基于接口的动态代理----JDK动态代理
基于类的动态代理--cglib
现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
这里使用JDK的原生代码来实现,其余的道理都是一样的!
代码实现
抽象角色和真实角色和之前的一样!
Rent . java 即抽象角色
//抽象角色:租房 public interface Rent { public void rent(); }
Host . java 即真实角色
//真实角色: 房东,房东要出租房子 public class Host implements Rent{ public void rent() { System.out.println("房屋出租"); } }
ProxyInvocationHandler. java 即代理角色
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); } // proxy : 代理类 method : 代理类的调用处理程序的方法对象. // 处理代理实例上的方法调用并返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); //核心:本质利用反射实现! Object result = method.invoke(rent, args); fare(); return result; } //看房 public void seeHouse(){ System.out.println("带房客看房"); } //收中介费 public void fare(){ System.out.println("收中介费"); } }
Client . java
//租客 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.rent(); } }
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); } // proxy : 代理类 // method : 代理类的调用处理程序的方法对象. 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 methodName){ System.out.println("执行了"+methodName+"方法"); } }
测试!
public class Test { public static void main(String[] args) { //真实对象 UserServiceImpl userService = new UserServiceImpl(); //代理对象的调用处理程序 ProxyInvocationHandler pih = new ProxyInvocationHandler(); pih.setTarget(userService); //设置要代理的对象 UserService proxy = (UserService)pih.getProxy(); //动态生成代理类! proxy.delete(); } }
动态代理的好处
-
静态代理有的它都有,静态代理没有的,它也有!
-
可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
-
公共的业务由代理来完成 . 实现了业务的分工 ,
-
公共业务发生扩展时变得更加集中和方便 .
-
一个动态代理 , 一般代理某一类业务
-
一个动态代理可以代理多个类,代理的是接口!