SpringIOC
常见的几个注解
扫描注解
使用前需要勾选上
base-package 需要扫描的位置的路径
一、@Component
@Component是一个元注解,当使用基于注解的配置和类路径扫描的时候,这些类就会被实例化。就是说它注解的类会在contex配置扫描到的时候加载,没有被他注解的则不会加载。
二、@Controller
@controller是一个用于控制层的注解,用来描述此处为控制层,他与@Component使用方面暂时无区别
三、@Service
@Service 是一个用于业务层的注解,用来描述此处是业务层,他与@Component使用方面暂时无区别
四、@Repository
@Repository是一个用于持久层(daoimp)的注解,用来描述此处是持久层,他与@Component使用方面暂时无区别
五、@Resource
@Resource 会根据name属性去IOC中寻找对应,昨天的XML文件配置后,使用时需要进行Setter设置,而此注解就是免除Setter设置
六、@Autowired
@Autowired 默认会根据类型去IOC容器中查询对应的对象,如果想根据name查找则需要用到@Qualifier(“name”)
七、@Qualifier
@Qualifier 配合@Autowired使用
JAVA配置的实现
在spring4以候,提供了基于JAVA类的方式实现替代XML文件
一、代码案例
其一
@Configuration // 该注解表示该类相当于一个XML文件,等价于 ApplicationContext.xml
public class JavaConfig {
@Bean // 该注解理解为注册,就相等于XML文件中 <bean class="User" name="getuser" />
public User getuser(){
User user=new User();
return user;
}
}
运行代码
public static void main(String[] args) {
ApplicationContext ac=new AnnotationConfigApplicationContext(JavaConfig.class);
//注意此处需要用 AnnotationConfigApplicationContext 方法加载
User bean = ac.getBean(User.class);
bean.hello();
}
其二
@Configuration
//@ComponentScan("com.sxt.pojo") //在需要扫描的类头部开启扫描
//@ComponentScans
(value={@ComponentScan("com.sxt.pojo")........})
//与上方式一样,此处为扫描多个
public class JavaConfig {
@Bean
public User getuser(){
User user=new User();
return user;
}
}
@Component //此注解意为:扫描这个类
public class User {
public void hello() {
// TODO Auto-generated method stub
System.out.println("hello");
}
public static void main(String[] args) {
ApplicationContext ac=new AnnotationConfigApplicationContext(JavaConfig.class);
User bean = ac.getBean("user",User.class); //这里的user为User类名,User类名全部大写则这里也
//需要大写,全部小写则全部小姐,首字母大写则全部小写
bean.hello();
}
使用JAVA代替XML配置在SpringBoot中被广泛应用
Profile
用于方便在各种环境中切换
一、存储
二、配置
@Configuration
public class JavaConfig {
@Bean
@Profile("dev")
public DataSourceUtils getDevDataSource() {
return new DataSourceUtils("http://192.168.1.110", "admin", "admin");
}//其一 开发环境
@Bean
@Profile("pro")
public DataSourceUtils getProDataSource() {
return new DataSourceUtils("http://192.168.1.120", "root", "root");
}//其二生产环境
@Bean
@Profile("test")
public DataSourceUtils getTestDataSource() {
return new DataSourceUtils("http://192.168.1.119", "test", "test");
}//其三测试环境
}
三、测试
public class TestProfile {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac=new AnnotationConfigApplicationContext();
ac.getEnvironment().setActiveProfiles("dev");
ac.register(JavaConfig.class);
ac.refresh();//刷新
DataSourceUtils bean = ac.getBean(DataSourceUtils.class);
System.out.println(bean);
}
}
以上的内容会穿插在开发当中,java配置的实现以及一些常见的注解,会使用,理解就行。而对于以下内容则需要掌握。
Bean的作用域
在Bean的定义的时候,bean有一个属性scope,他有4个值,prototype、request表示一次请求有效、session表示一次会话有效、singleton这个值是默认的,即为单例模式,它表示只会有一个对象,当用容器多次获取时都是这一个对象。而protorype类型则表示有多个对象它是原型模式,当容器多次获取时会有不同的地址。
三种设计模式
一、单例模式
单例模式的核心是保证一个类只有一个new出来的对象,并且是他自己创建出来的,并且提供一个访问的全局节点
单例模式它有一系列的实现方式:
使用单例模式的目的是为了保证一个类只有一个实例的存在
二、原型模式
使用原型模式可以帮助我们解决大量new关键字来创建对象的烦恼。原型模式我们也称为克隆模式,即一个某个对象为原型克隆出来一个一模一样的对象,该对象的属性和原型对象一模一样。而且对于原型对象没有任何影响。原型模式的克隆方式有两种:浅克隆和深克隆
1、浅克隆
克隆的对象必须实现Cloneable,Serializable这两个接口
然后重写克隆方法就行
然后克隆
浅克隆模型图
假设在克隆出来的新对象中需要修改某个属性则直接set修改就行
但是随之而来的就是克隆导致的问题:虽然产生了两个完全不同的对象,但是被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。即原对象没有被克隆
所以就需要用到深克隆
2、深克隆
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深克隆就是把所有关系都复制了一遍,而不是像浅克隆那样只复制了表面。
深克隆模型图
而深克隆又分为两种方式
第一种方式:在浅克隆的基础上实现深克隆
在克隆时将birth属性也克隆一份,如果需要多个则将多个一一克隆,即需要多少个引用,则克隆多少个。
第二种方式:通过序列化和反序列化实现深克隆
将原对象存贮进文件中,即序列化
然后再将对象从文件中读取出来,即反序列化,这时候读取出来的对象是一个新的对象,这样就达到了深度克隆的目的—>产生另一个对象但是是和原对象相同的属性
使用原型模式和直接new对象方式的比较
原型模式适用于我们需要大量生产同一类型对象的时候,他相比于传统直接new的方式会更加的快。
举例:用两种方式生产1000个对象
所以通过clone的方式在获取大量对象的时候性能开销基本没有什么影响,而new的方式随着实例的对象越来越多,性能会急剧下降
。
三、代理模式
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
1、静态代理
案例:购买科技枪
在某个地方有一个商店
public interface PatternStaticService {
public String shop(String msg) ;//商店
}
然后在商店里有一件我想要的东西
public class PatternStaticServiceImp implements PatternStaticService{
@Override
public String shop(String msg) {
System.out.println("我想要的是:"+msg);
return msg;
}
}
然后我开始了我的购买之路
public class TestService {
public static void main(String[] args) {
//几经周折我寻找到了商店的位置(获取目标对象)
PatternStaticService target=new PatternStaticServiceImp();
//然后找到了老板(寻找代理对象)
PatternProxy proxy=new PatternProxy(target);
//我跟老板说我要一把科技枪(让代理对象去执行增强方法)
String hello = proxy.shop("科技枪");
//然后老板给了我一把科技枪,说他帮我增强了一波(最终返回值)
System.out.println(hello);
}
}
然后我问老板是怎么增强的,老板告诉我说
public class PatternProxy implements PatternStaticService{
private PatternStaticService tagert;//声明成员变量,即为找到目标对象
//通过构造的方式传递
public PatternProxy(PatternStaticService tagert) {
super();
this.tagert = tagert;
}
@Override
public String shop(String msg) {
System.out.println("告诉老板我的需求");
String msg1 = tagert.shop(msg);
System.out.println("老板知道之后,在我的需求上增强了一下");
return "给了我一把:增强型"+msg1;
}
}
然后就有了结果
但是如果用这种方法的话,每当我需要增强一种功能的时候我就需要在接口上写,多了的时候就很繁杂,这样不利于我们后期 的管理修改。
所以就引入动态代理
2、动态代理
动态代理又分两种类型:
①、JDK中的动态代理
动态代理中我们会用到一个方法:java.lang.reflect.Proxy 包下的
Proxy.newProxyInstance(loader, interfaces, h)
loader:类加载器
interfaces:接口数组
h:方法调用处理器
接口中定义两个目标对象
public interface PatternStaticService {
public String shop(String msg) ;
public void fun(String msg) ;
}
实现定义的两个目标对象
public class PatternStaticServiceImp implements PatternStaticService{
@Override
public String shop(String msg) {
System.out.println("传递过来的是:"+msg);
return msg;
}
@Override
public void fun(String msg) {
System.out.println("传递过来的是:"+msg);
}
}
主方法
public static void main(String[] args) {
//获取目标对象
PatternStaticService target=new PatternStaticServiceImp();
//通过JDK动态获取代理对象
PatternStaticService proxy = (PatternStaticService) //这里向上转型
Proxy.newProxyInstance(target.getClass().getClassLoader()
, target.getClass().getInterfaces()//实现目标对象的所有接口
, new InvocationHandler() {
/**
* 动态代理核心方法,当代理对象执行的时候具体执行的方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理对象名称:"+method.getName());//得到目标方法的名称
System.out.println("执行动态代理之前");
Object obj=method.invoke(target, args);
System.out.println("执行动态代理之后");
if (obj!=null&&obj instanceof String) {
String msg=(String) obj;
return msg.toUpperCase();
}
return null;
}
});
//通过代理对象执行目标对象的方法
String shop = proxy.shop("aaa");
System.out.println("返回的结果是:"+shop);
proxy.fun("bbb");//他没有返回值
}
运行后结果是:
②、cglib中的动态代理
使用这种方式需要导入一个第三方依赖包
目标对象
public class PatternStaticServiceImp {
public String shop(String msg) {
System.out.println("目标对象是:"+msg);
return "给-"+msg+"-附了魔";
}
public void fun(String msg) {
System.out.println("目标对象是:"+msg+" 但是我没有给他附魔");
}
}
创建代理工厂
/**
* 这是一个代理对象的工厂类,他用来生产创建代理对象
*/
public class CglibFactory implements MethodInterceptor{
//目标对象
private PatternStaticServiceImp target;
//构造方法
public CglibFactory(PatternStaticServiceImp target) {
super();
this.target = target;
}
/**
* 创建代理对象的方法
*/
public PatternStaticServiceImp createProxyInstane(){
//获取Enhancer对象
Enhancer enhancer=new Enhancer();
//告诉代理对象他的父类是谁
enhancer.setSuperclass(PatternStaticServiceImp.class);
//设置回调的对象
enhancer.setCallback(this);
//返回代理对象
return (PatternStaticServiceImp) enhancer.create();
//因为父类是UserServiceImp类型,所以就需要向上转型
}
/**
* 代理对象具体执行的方法
*/
@Override
public Object intercept(Object arg0, Method method, Object[] args,
MethodProxy arg3) throws Throwable {
System.out.println("执行代理之前:");
Object obj = method.invoke(target, args);
System.out.println("执行代理之后:");
System.out.println(obj);
return obj;
}
}
运行
public class TestCglibProxy {
public static void main(String[] args) {
PatternStaticServiceImp target=new PatternStaticServiceImp();
CglibFactory factory=new CglibFactory(target);
PatternStaticServiceImp proxy = factory.createProxyInstane();
proxy.shop("科技枪");
proxy.fun("无用大棒");//没有返回
}
}
结果
静态代理的话在接口中有多少个方法就需要重写多少次,而动态代理则免除了多次创建代理的烦恼。