Java动态代理

java中的代理有静态代理和动态代理。

java中规定,动态代理的对象必须要有实现一个接口,因为是为动态创建了一个这个接口的实现类,

在JDK1.5之后提供了一个”java.lang.reflect.Proxy”类,通过”Proxy”类的newProxyInstance静态方法创建一个接口的代理对象,这个代理对象可以调用接口的方法。

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

newProxyInstance方法返回一个代理对象,这个方法总共有3个参数:
ClassLoader指明生成代理对象使用哪个接口类装载器,
Class<?>[]指明生成哪个接口的代理对象,
InvocationHandler用来指明产生的这个代理对象要做什么事情。

package com.test;

/**
 * Created by admin on 2018/2/4.
 */
public interface StarMan {

    String sing(String name);
}


package com.test;

/**
 * Created by admin on 2018/2/4.
 */
public class LiuDeHua implements StarMan {
    public String sing(String name){
        System.out.println("刘德华唱"+name+"歌!!");
        return "歌唱完了,谢谢大家!";
    }
}
package com.test;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by admin on 2018/2/4.
 */
public class ProxyOrg {
    public static StarMan getStarManProxy(String starName) {
        Class<?>[] interfaces={StarMan.class};
        return (StarMan) Proxy.newProxyInstance(StarMan.class.getClassLoader(), interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result=null;

                if (method.getName().equals("sing")) {
                    System.out.println("我是经纪人,找明星唱歌得先给十万块钱!!");

                    if(starName.equals("刘德华")){
                        StarMan starMan = new LiuDeHua();
                        result = method.invoke(starMan, args); //代理对象调用真实目标对象的sing方法去处理用户请求
                    }else{
                        System.out.println("联系不到"+starName);
                    }
                }

                return result;
            }
        });
    }
}
package com.test;

/**
 * Created by admin on 2018/2/4.
 */
public class Main {

    public static void main(String[] args) {
        StarMan p = ProxyOrg.getStarManProxy("刘德华");
        String retValue = p.sing("冰雨");
        System.out.println(retValue);

        p = ProxyOrg.getStarManProxy("黄家驹");
        retValue = p.sing("海阔天空");
        System.out.println(retValue);
    }
}

JDK动态代理和CGLIB动态代理

JDK动态代理只能针对实现了接口的类生成代理(实例化一个类)。此时代理对象和目标对象实现了相同的接口,目标对象作为代理对象的一个属性,具体接口实现中,可以在调用目标对象相应方法前后加上其他业务处理逻辑。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类(没有实例化一个类),覆盖其中的方法。

Java反射

Java新功能

Java5

1.泛型。通用对象常设计为Object对象,具体使用时再强制类型转换,JDK 5.0通过泛型很好的解决了这个问题。
2.泛型擦除和运行时泛型获取
Java 5 引入了泛型,不管是 ArrayList 还是 ArrayList,在编译时都会被编译器擦除成了 ArrayList。Java 在创建泛型实例时不创建新的类,从而避免运行时的过度消耗。
获取泛型参数,也仅仅获得了声明时泛型参数占位符。getTypeParameters 方法的 Javadoc 也是这么解释的:仅返回声明时的泛型参数。所以通过 getTypeParamters 无法获得运行时的泛型信息。
基本数据类型是无法泛型化的。

List list1 = new ArrayList();  
List list2 = new ArrayList();   
System.out.println(list1.getClass() == list2.getClass());//true

List list = new ArrayList();  
Map<Integer, String> map = new HashMap<Integer, String>();  
System.out.println(Arrays.toString(list.getClass().getTypeParameters()));//[E] 
System.out.println(Arrays.toString(map.getClass().getTypeParameters())); //[K, V] 

获取泛型类型的方法:
Java 引入泛型擦除的原因是避免因为引入泛型而导致运行时创建不必要的类,那么可以通过定义类的方式,在类信息中保留泛型信息。 即可以在变量定义后面加一对大括号从而创建一个匿名内部类。 所以,Java 的泛型擦除是有范围的,即类定义中的泛型是不会被擦除的。

Map<String, Integer> map = new HashMap<String, Integer>() {};  
Type type = map.getClass().getGenericSuperclass();  
ParameterizedType parameterizedType = ParameterizedType.class.cast(type);  
for (Type typeArgument : parameterizedType.getActualTypeArguments()) {  
    System.out.println(typeArgument.getTypeName());  //java.lang.String ,java.lang.Integer 
}  
ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference() {});  

Java7

1.try-with-resource
在C++中可以把关闭资源的代码放在析构函数中,在C#中有using代码块,让外部资源的关闭行为与外部资源的句柄对象的生命周期关联,当外部资源的句柄对象生命周期终结时(例如句柄对象已出作用域),外部资源的关闭行为将被自动调用。这样不仅更加符合面向对象的编程理念(将关闭外部资源的行为内聚在外部资源的句柄对象中),也让代码更加简洁易懂。
在Java编程过程中,如果打开了外部资源(文件、数据库连接、网络连接等),由于外部资源不由JVM管理,无法享用JVM的垃圾回收机制,所以必须在这些外部资源使用完毕后,手动关闭它们。
在JDK7以前,Java没有自动关闭外部资源的语法特性,直到JDK7中新增了try-with-resource语法。当一个外部资源的句柄对象(比如FileInputStream对象)实现了AutoCloseable接口,就可以使用try-with-resource语法形式,消除板式代码更优雅的关闭资源,将句柄对象的创建放在try关键字后面的括号中,当这个try-catch代码块执行完毕后,Java会确保句柄的close方法被调用。try-with-resource不是JVM虚拟机的新增功能,只是JDK实现的一个语法糖。
当一个外部资源的句柄对象实现了AutoCloseable接口,JDK7中便可以利用try-with-resource语法。

try(MyTableChartFactory vtfFactory = new MyTableChartFactory()) {
                            vtfFactory.init();
                            vtfFactory.createVTF(algorithm, true, vtfHtml, tempPath, image221);
                            vtfFactory.createVTF(algorithm, false, vtfHtml, tempPath, image222);
                        }

2.改善异常处理,允许在同一个 catch 块中捕获多个异常。
3.Fork-Join 池某种程度上实现 Java 版的 Map-reduce。
4.允许 Switch 中有 String 变量和文本。但这仅仅是一个语法糖。内部实现在 switch 中使用字符串的 hash code。
5.菱形操作符(<>)用于类型推断。不再需要在变量声明的右边申明泛型,可以写出可读写、更强、更简洁的代码。

Spring Boot

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。Spring Boot 遵循“约定优于配置”原则,为Spring平台及第三方库提供开箱即用的设置,大部分可以使用默认配置。Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务
Springboot 暴露 HTTP 服务,并走 JSON 模式。

spring-boot文档
https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/

HttpClient的3种超时
http://blog.csdn.net/hudashi/article/details/8652482

Spring Boot参考指南 Spring-Boot-Reference-Guide
https://qbgbook.gitbooks.io/spring-boot-reference-guide-zh/

运行TEST:打开Test代码文件,右键或run菜单中就变成test了。

Spring Boot热部署
http://www.imooc.com/learn/915

SpringBoot开发详解(一)–初识SpringBoot
http://docs.spring.io/spring-boot/docs/current/reference/html/

RestTemplate

Spring Boot提供了RestTemplate来发起REST请求,RestTemplate提供了REST风格的api,默认通过JDK自带的HttpUrlConnection作为底层的HTTP发送方式,使用Jackson来序列化Json数据。
RestTemplate 调用传参
http://blog.csdn.net/itlqi/article/details/49592985
url路径可以加入路径参数{参数名}格式,在传参数时可以传Map或数组或其它类型。

如果期望调用结果返回List,则不能简单调用xxxForObject,因为存在泛型的类型擦除,RestTemplate在反序列化时不知道实际类型,这时需要使用ParameterizedTypeReference来包含泛型类型,并通过exchange通用接口来调用。
RestTemplate.getForObject将PO中List的泛型变成了LinkedHashMap,造成ClassCastException。可以使用exchange 基础调用接口代替getForObject来解决问题。

ResponseEntity<List<AreaProto>> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, new ParameterizedTypeReference<List<AreaProto>>() {});
List<AreaProto> areas=responseEntity.getBody();

注意到创建ParameterizedTypeReference的对象是用{}结束的,因为这里是创建了一个ParameterizedTypeReference的子类,依据在类定义中的泛型信息保留的原则,子类对象保留了期望返回的泛型List。
除了使用ParameterizedTypeReference来保留泛型信息,还可以通过getForObject先映射成String,再通过Jackson ObjectMapper转为指定类型。

相比getForObject,getForEntity返回信息中包含了HTTP头信息。
通过 Spring RestTemplate 调用带请求体的 Delete 方法(Delete With Request Body)
https://www.cnblogs.com/snake-hand/p/3151391.html

定制RestTemplate,通过实现RestTemplateCustomizer接口,并声明为配置类,在其中可以对RestTemplate的参数进行全局设置。还可以通过添加okhttp的依赖来使用okhttp来作为底层http发送方式。

缓存

Spring Boot 通过方法注解的方式使用统一的缓存。
@CachePut 注释可以确保方法被执行,同时方法的返回值也被记录到缓存中。
@CacheEvict 注释将删除缓存数据,可以指定一个或多个缓存。还提供参数allEntries表示是否需要清除缓存中的所有元素,默认为false。当指定了allEntries为true时,Spring Cache将忽略指定的key。还有一个属性 beforeInvocation,缺省为 false,即在实际的方法执行完成后,才对缓存进行清空操作。如果执行方法出现异常,则会导致缓存清空不被执行。
@Cacheable 标注的方法,如果其所在的类实现了某个接口,那么该标注所在方法也必须是接口里面的方法,否则cache无效。因为Spring把实现类装载为Bean时会用代理包装,所以从Spring Bean的角度看,只有接口里面的方法是可见的
解决办法:把待cache的方法移到接口里面,或者把@Cacheable标注在一个没有实现任何接口的类的public方法上。因为这种Bean也被Spring产生了代理,看得到的只有public方法。
@Transactional同@Cacheable一样,把它们加于private方法也无效。
Spring 在执行 @Cacheable 标注的方法前先查看缓存中是否有数据,如果有则直接返回缓存数据,不再执行方法体;若没有数据,执行该方法并将方法返回值放进缓存。
@Cacheable 支持的参数有:
value:缓存区名称,不能为空。理解为表。
key:缓存的key,默认为空。理解为记录的id。
condition:触发条件,只有满足条件的情况才会加入缓存,默认为空。
unless:用来是决定是否添加到缓存。与condition不同的是,unless表达式是在方法调用之后进行评估的。如果返回false,才放入缓存(与condition相反)。 #result指返回值 例:

@Cacheable(value="DeviceRecords", key = "#deviceCode", unless="#result == null")
    public DeviceRecord getDeviceByCode(String deviceCode){
        return deviceRecordRepository.findOneByCode(deviceCode);
    }

Spring Boot消息支持

Spring对JMS和RabbitMQ的支持分别提供了JmsTemplate和RabbitTemplate来发送消息,提供了@JmsListener、@RabbitListener注解在方法上监听消息代理发布的消息,分别需要通过@EnableJms和@EnableRabbit来开启支持。
Spring Boot进一步自动配置开启了对@EnableJms、@EnableRabbit、JmsTemplate、RabbitTemplate的支持,在application.properties中分别以spring.activemq和spring.rabbitmq来配置所需的属性。

Spring Security

对Spring Security的理解:把对REST资源的访问权限与用户角色关联,即Spring Security根据用户角色控制当前登录用户是否可以访问这个REST URL。
permitAll()允许请求没有任何的安全限制。
authenticated()要求在执行该请求时必须已登录。
authorizeRequests()方法返回的对象还有更多的方法用于细粒度地保护请求。
自定义处理请求过滤可以使用FilterRegistrationBean:

@Bean
	public FilterRegistrationBean jwtFilterRegistrationBean(){
		HttpAuthFilter httpAuthFilter=new HttpAuthFilter();
		FilterRegistrationBean registrationBean = new FilterRegistrationBean();
		registrationBean.setFilter(httpAuthFilter);
		registrationBean.setUrlPatterns(httpAuthFilter.getUrlPatterns());

		return registrationBean;
	}

重拾后端之Spring Boot(四):使用JWT和Spring Security保护REST API
https://www.jianshu.com/p/6307c89fe3fa
Spring Security(01)——初体验
http://elim.iteye.com/blog/2154299
SpringSecurity学习笔记之四:拦截请求
http://blog.csdn.net/zhoucheng05_13/article/details/60467234

查看 Tomcat版本

工程中pom文件中parent标签中指定了父级依赖,它用来提供相关的Maven默认依赖。例如Spring Boot 1.5.1 默认提供了哪些依赖,可查看.m2\repository\org\springframework\boot\spring-boot-dependencies\1.5.1.RELEASE\spring-boot-dependencies-1.5.1.RELEASE.pom。
可以在工程中覆盖tomcat版本:

<properties> 
 <tomcat.version>8.0.29</tomcat.version> 
</properties>

在容器启动的时候执行一些内容

在容器启动的时候执行一些内容,比如:读取配置文件信息,数据库连接,删除临时文件,清除缓存信息,在Spring框架下是通过ApplicationListener监听器来实现的。在Spring Boot中提供了两个接口实现这样的需求:CommandLineRunner和ApplicationRunner,它们的执行时机为容器启动完成的时候。

Spring Boot之CommandLineRunner和ApplicationRunner【从零开始学Spring Boot】
https://blog.csdn.net/gebitan505/article/details/55047819

SpringBoot之退出服务(exit)时调用自定义的销毁方法

两种方法:实现DisposableBean接口,或者@PreDestroy方法注解。测试在CMD中用java -jar启动服务,用ctrl+c退出,销毁方法正常被调用。而如果用的mvn spring-boot:run来启动运行,可能不会执行销毁的操作。
https://blog.csdn.net/zknxx/article/details/52204036

WebMvcConfigurer

WebMvcConfigurer接口用来全局定制Spring boot的MVC特性,可以通过实现这个接口并声明为java配置@Configuration ,来配置一些特性,例如拦截器、跨域访问、类型转换、以及注册Controller。

验证框架

JSR-303是java标准的验证框架,它定义了一系列注解用来验证Bean的属性,Hibernate Validator实现了这个标准。
在Controller中,在方法的对象参数前加上@Validated注解可触发校验。

jackson

Spring boot内置了jackson来完成JSON的序列化和反序列化操作。jackson是一个流行的高性能javabean(pojo)到json的绑定工具,它使用ObjectMapper来完成。json到pojo称为反序列化,Pojo到json称为序列化。
另外,Fastjson是阿里提供的。
有三种层次的序列化方式:底层方式、树遍历方式、绑定方式。
使用以下方式会影响全局,还可能会导致一些转换失败而使请求失败。
例如日期时间格式化:

  @Bean
  public ObjectMapper getObjectMapper(){
      ObjectMapper objectMapper=new ObjectMapper();
      objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

      return objectMapper;
  }

使用jackson注解,可以用来个性序列化操作。例如日期时间格式化:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
Date createTime;

在使用模板类时,使此注解生效则需要在方法返回类型中使用模板全声明形式,例如

//通告列表
@RequestMapping(value = "/notices",method = RequestMethod.GET)
ResponsePagedData<NoticeProto> notices(int pageSize, int pageNo){

PageInfo<NoticeEntity> pageInfo=daoService.findNotices(pageSize,pageNo);
List<NoticeEntity> entities=pageInfo.getList();
long total=pageInfo.getTotal();

List<NoticeProto> itemProtos=new ArrayList<>();

if(entities!=null){
entities.forEach(record -> {
itemProtos.add(record.getProto());
});
}

ResponsePagedData proto=new ResponsePagedData();
proto.setTotal((int)total);
proto.setList(itemProtos);

return proto;
}

Spring-mongodb

Spring Boot中访问数据库

1.JdbcTemplate,是Spring在JDBC的基础上做了一些封装,面向SQL的访问数据库方式。
2.使用Spring-data-jpa在JPA提供的简单语义上做了一定的封装,整合orm框架访问数据库,默认采用Hibernate实现。Spring Data是面向对象的访问数据库方式。
JPA:Java Persistence API,java持久化api。
Repository 抽象了对数据库的操作,提供了以下接口:CrudRepository、PagingAndSortingRepository、JpaRepository。
注解查询:@Query中允许使用JPQL或SQL来给出查询方法。

Spring-data-mongodb

Spring-data对MongoDB进行了很好的支持,包括以下特征:
mongodb在spring中的配置支持注解 @Configuration和xml的方式。
MongoTemplate辅助类扩展了Mongo operations的生产效率,提供了document和pojo类型之间的转换。MongoOperations是MongoTemplate实现的接口,MongoOperations中有很多操作文档的方法。

参考:
http://docs.spring.io/spring-data/data-document/docs/current/reference/html/
Spring-mongodb 查询
http://docs.spring.io/spring-data/mongodb/docs/1.2.0.RELEASE/reference/html/mongo.repositories.html
http://blog.csdn.net/congcong68/article/details/47183209
Spring Data与MongoDB:不协调的设计,及评论
http://www.infoq.com/cn/news/2013/11/spring-data-mongo-mismatch
MONGODB中的分组聚合在JAVA中的用法
http://www.debugrun.com/a/1pcWFsT.html

MongoRepository

如果只需要用简单的CRUD操作,则不需要添加其他的方法,MongoRepository提供的方法足够使用。
MongoRepository接口有两个参数:第一个参数是带有@Document注解的对象类型,第二个参数是带有@Id注解的属性类型。

public interface PersonRepository extends MongoRepository<Person, String>{
}
注解 描述
@Document(collection=”name1″) 标示映射到mongoDB文档上的领域对象
@ID 标示某个为ID域
@DbRef 标示某个域要引用其他的文档,这个文档有可能位于另外一个数据库中
@Field(“field1”) 为文档域指定自定义的元数据,若不使用@Field注解,域名与Java属性相同
@Version 标示某个属性用作版本域

 

@Query注解指定查询:
使用@Query时,方法名可不按照规则来写,这可能自由缩短条件多的查询方法名。
@Query注解中的内容是一个 JSON 对象,和在 MongoDB 的控制台查询的find()中的内容是一样的,只不过将双引号换成单引号,将需要变量用 [0]、[1] 和 [2] 的形式表示第一、第二和第三个参数。?#{} 是表示里面的内容是个 SpEL (Spring 的表达式语言) 表达式。

import org.bson.types.ObjectId;
public interface PersonRepository extends MongoRepository<Person, ObjectId> {
    //根据实体类中的属性进行模糊查询带分页,同时指定返回的键:
    /*
    value是查询的条件,?0是占位符,对应着方法中参数中的第一个参数,如果对应的是第二个参数则为?1。
    fields指定返回字段,其中id是自动返回的,不用指定。bson中{'name':1}中的1代表true,表示返回此字段。
    */
    @Query(value="{'name':?0}",fields="{'name':1}")
    public Page findByNameLike(String name,Pageable pageable);

    //查询所有id不为空的数据,同时指定返回的键。方法名的定义规则为:find + By + 属性名(首字母大写) + NotNull。
    @Query(value="{'_id':{'$ne':null}}",fields="{'name':1}")
    public Page findByIdNotNull(Pageable pageable);
}

当MongoDB遇上Spring
http://blog.csdn.net/u012150370/article/details/51822776
A Guide to Queries in Spring Data MongoDB

A Guide to Queries in Spring Data MongoDB

distinct

mongoTemplate.getCollection("collection_name").distinct("field", new BasicDBObject("price", new BasicDBObject("$gt", 10)));
Criteria criteria = getCriteria(conditions, "and");
List values = mongoTemplate.getCollection(collectionName).distinct(distinctField, new Query(criteria).getQueryObject());
String jsonSql="{distinct:'terminalRecord', key:'areaId'}";
CommandResult commandResult=mongoTemplate.executeCommand((DBObject) JSON.parse(jsonSql));
DBObject dbObject=BasicDBObjectBuilder.start("distinct",collectionName).add("key",distinctKey).get();
CommandResult commandResult=mongoTemplate.executeCommand(dbObject);
List values=(List)commandResult.get("values");

Spring Boot Starter

传统Maven项目中通常将一些层、组件拆分为模块来管理,以便相互依赖复用。在Spring Boot项目中我们则可以创建自定义Spring Boot Starter来达成该目的。

配置pom

自定义starter并且通过spring-boot-autoconfigure完成自动化配置。
在pom.xml中配置对spring-boot-autoconfigure的依赖,如果继承了spring-boot-starter-parent,则可以不配置。

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-autoconfigure</artifactId>
 </dependency>

配置映射参数实体

Spring Boot提供了一个注解@ConfigurationProperties,该注解可以完成将application.properties配置文件内的有规则的配置参数映射到实体内的field内,实体需要提供setter方法。

import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "前缀")
public class HelloProperties
{
    //消息内容
    private String msg = "default message";
}

编写业务类

实现自动化配置

自动化配置提供实体bean的验证以及初始化。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration//开启配置
@EnableConfigurationProperties(HelloProperties.class)//开启使用映射实体对象
//初始化该配置类的多个条件注解
@ConditionalOnClass(HelloService.class)
@ConditionalOnProperty(
                prefix = "hello",//存在配置前缀hello
                value = "enabled",//开启
                matchIfMissing = true//缺失检查
)
public class HelloAutoConfiguration
{
    @Autowired
    private HelloProperties helloProperties;

    @Bean
    @ConditionalOnMissingBean(HelloService.class)
    public HelloService helloService()
    {
        HelloService helloService = new HelloService();
        helloService.setMsg(helloProperties.getMsg());//设置消息内容

        return helloService;
    }
}

指明自动化装配位置

在注解@SpringBootApplication中已经存在一个开启自动化配置的注解@EnableAutoConfiguration来完成自动化配置。@EnableAutoConfiguration内部使用了EnableAutoConfigurationImportSelector,继而使用了SpringFactoriesLoader.loadFactoryNames方法进行扫描具有META-INF/spring.factories文件的jar包。

我们在src/main/resource目录下创建META-INF目录,新建文件spring.factories,写入如下内容:

#配置自定义Starter的自动化配置,多个值可用逗号分隔。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xxx.HelloAutoConfiguration

@Conditional条件判断注解

Spring 4.X 新加入了@Conditional注解,控制某些configuration是否生效。除了自己自定义Condition,Spring还提供了很多ConditionalOn注解。这些注解里的条件可以是多个,可以赋默认值,也可以标注在类上。如果标注在类上,则对类里的所有@Bean方法都生效。

@ConditionalOnProperty(value = {"abc.property"}, matchIfMissing = false) #其name属性用来从application.properties中读取某个属性值,如果该值为空,则返回false。如果值不为空,则将该值与havingValue属性指定的值进行比较返回true或false。

开发中热部署

LiveReload Server,spring-boot-devtools