有一天晚上我脑海中倏忽冒出来一个题目:“如何治理我们代码中的对象”。
小弈是刚事情时的我,他说:经由过程 new 来建立一个对象然后直接运用就好了啊。
public class HelloWorld { public void hello() { System.out.println("hello world!"); } } HelloWorld helloWorld = new HelloWorld(); helloWorld.hello();
你们看,我有一个 HelloWorld 类,我用 new 就可以直接建立一个对象,然后就可以运用这个对象中一切的要领了,多简朴啊。
二弈是事情两年的我,他一脸蔑视的对小弈说,你别成天 HelloWorld 好不好,另有啊,除了 new 你就不会其他的了,能不能有点寻求啊?
小弈对二弈说那你说除了 new 另有什么方法啊?
二弈说可以经由过程 Class 的 newInstance 或许 Constructor 的 newInstance 来建立对象实例啊。
不过你得记着,Class 的 newInstance 只能对那些具有可见的(Accessible)无参组织函数的类,才举行对象的实例化,而 Constructor 就没有这些限定。
大弈是事情三年的我,他说,虽然你们的要领都可以用来建立对象,但都照样手动建立的,太原始了,生产力太低。
工欲善其事,必先利其器,我们也得找个高效的生产力东西。IOC 容器你们相识吧?
之前我们在一个对象中如果要挪用别的一个对象的要领时,都是经由过程 new 或许反射来手动建立该对象,然则每次都如许做太累了,而且类之间的耦合也很高。
经由过程 IOC 容器,我们可以把一切的对象交给容器来治理,在运用之前只须要定义一下对象,然后再运用到该对象时,IOC 容器就会帮我们把该对象初始化好,如许是不是是更轻易呢?
大弈说完,举了一个例子:
@Bean public class RegisterService { public void register() { // do register } } @Bean public class LoginService { public void login() { // do login } } @Bean public class HelloWorld { @Autowired private RegisterService registerService; @Autowired private LoginService loginService; public void hello() { // 注册 registerService.register(); // ... // 登录 loginService.login(); } }
IOC 容器经由过程一种叫 Bean 的注解,在体系启动时扫描一切经由过程 Bean 标注的类,对这些类举行实例化,然后将一切的对象都保存在容器中。再扫描一切经由过程 Autowired 标注的属性或许要领,从容器中找到与之婚配(经由过程称号或许范例等)的对象将细致的对象赋值给这些属性。如许我们就可以直接将这些对象拿来运用了,作为一个伸手党是不是是很幸运啊。
老弈是事情五年的我,他听了大弈的话后,提出了一个题目,关于新的项目可以运用这类 IOC 的容器,但是关于那些遗留的老项目来讲,要运用 IOC 来革新是不太相符真相的。
我举个例子,在一个遗留的老项目中,有一个中心的接口 Handler:
public interface Handler<REQ, RES> { RES handle(REQ request); }
Handler 接口有许多的完成类,我们须要对差别的要求来挪用差别的 Handler 完成类举行处置惩罚,如果用 IOC 容器来治理这些完成类,明显不太适宜,由于我们处置惩罚之前是不晓得该用哪一个 Handler 完成类的。
大弈想了想,如果 Handler 接口只需几个牢固的完成类,而且在运用时只会运用一个来举行处置惩罚,那末却是可以在启动前经由过程设置的体式格局来肯定细致运用哪一种 Handler ,比方可以经由过程 @Conditional 依据某些前提来肯定加载细致的对象,然则这类要在运用时才肯定 Handler 对象的范例确切比较辣手。
老弈看人人都不措辞了,就继续说了下去。
为了要在挪用要领时运用差别的 Handler 来处置惩罚差别的而要求,须要肯定两品种,一种是要求类,一种是处置惩罚类,而且要让要求类和处置惩罚类一一对应起来。
假定我们的要求类是一个 Packet 类,每一个细致的要求类都继续自这个基类。
那末想要肯定每一个细致的 Packet 是什么范例的,可以有许多种要领,可认为每一个 Packet 取一个唯一的名字,比方:
public abstract class Packet { public abstract String name(); }
也可认为每一个 Packet 指定一个标志,比方:
public abstract class Packet { public abstract int symbol(); }
然则不论哪一种体式格局,每一个 Packet 的完成类都须要完成抽象类中的要领,来“标志”本身是哪一种 Packet。
我们以第二种体式格局举例,假定我们有两个细致的 Packet:
public class RegisterPacket extends Packet { // 注册所须要的其他参数 int symbol() { return 1; } } public class LoginPacket extends Packet { // 登录所须要的其他参数 int symbol() { return 2; } }
如许当我们接收到 request 对象时,经由过程挪用 request.symbol() 就晓得这个 request 是哪一种范例的 Packet 了,这时候只需找到细致的 Handler 完成类来处置惩罚就可以了。
那要求类已可以肯定了,如何肯定 Handler 处置惩罚类呢?我们是不是也可以在 Handler 接口中定义一个 symbol 要领呢,像如许:
public interface Handler<REQ, RES> { int symbol(); RES handle(REQ request); }
如许的话,只需在一切的完成类中完成 symbol 要领来标注该 Handler 是用来处置惩罚何种 request 的即可。
public RegisterHandler implements Handler<RegisterPacket, RES> { int symbol(){ return 1; } RES handle(RegisterPacket request){ // 细致的处置惩罚要领 } } public LoginHandler implements Handler<LoginPacket, RES> { int symbol(){ return 2; } RES handle(LoginPacket request){ // 细致的处置惩罚要领 } }
末了把一切的 Handler 完成类都实例化后保存在一个 HandlerProvider 中,要运用时再到 HandlerProvider 中来猎取即可:
public interface HandlerProvider { Handler getHandler(int symbol); }
那如何猎取到一切的 Handler 的完成类呢,有两种要领。
一种是经由过程 ServiceLoader.load(Handler.class) 的体式格局来猎取,不过这类经由过程 spi 的体式格局须要在项目的 resources/META-INF/services/ 目录下建立一个 xxx.Handler 的文件,并在文件中将一切 Handler 的完成类的完整类限定符列出来。
另一种比较简朴的体式格局是经由过程扫描的体式格局,猎取到一切 Handler 的完成类。
到如今为止,我们的完成还算可以,然则有一个题目,那就是在 Handler 接口中我们增添了一个要领,如许做就对本来的代码举行了侵入。
为了让本来的代码坚持稳定,我们可以定义一个注解来标注在一切的 Handler 完成类上,比方如许:
@Symbol(1) public RegisterHandler implements Handler<RegisterPacket, RES> { RES handle(RegisterPacket request){ // 细致的处置惩罚要领 } } @Symbol(2) public LoginHandler implements Handler<LoginPacket, RES> { RES handle(LoginPacket request){ // 细致的处置惩罚要领 } }
如许就将 Handler 的完成和标注举行相识耦了,也可以经由过程扫描 @Symbol 注解来猎取到一切的 Handler 完成类,不过如许做的瑕玷就是如果我遗忘对某个 Handler 完成类增添 @Symbol 注解,到时候就猎取不到该 Handler 了。
人人听完老弈的话以后,都陷入了寻思,我靠,还可以这么玩,真风趣。
这时候候如今的我,也就是逅弈,说了一句,如果我有一个接口,他只需几个牢固的完成类,我不想搞那一套那末重的完成体式格局,然则我也须要动态的猎取完成类来对要求举行处置惩罚,那我该怎样办呢?
比方我有一个序列化的接口,以下所示:
public interface Serializer { byte[] serialize(Packet packet); }
然后只需五种细致的序列化的完成类,以下所示:
public class JdkSerializer implements Serializer { @Override public byte[] serialize(Packet packet) { // 细致的序列化操纵 } } public class FastJsonSerializer implements Serializer { @Override public byte[] serialize(Packet packet) { // 细致的序列化操纵 } } public class HessianSerializer implements Serializer { @Override public byte[] serialize(Packet packet) { // 细致的序列化操纵 } } public class KryoSerializer implements Serializer { @Override public byte[] serialize(Packet packet) { // 细致的序列化操纵 } } public class ProtoStuffSerializer implements Serializer { @Override public byte[] serialize(Packet packet) { // 细致的序列化操纵 } }
那末我们该怎样肯定运用哪一种序列化体式格局对参数 packet 举行序列化呢?
运用老弈方才说的那一套也确切可以完成,不过太贫苦了,又得对 Packet 定义 symbol,又得对 Hander 完成类举行标注,还得扫描一切的完成类。
我只需五个完成类,不须要搞那末贫苦的。
实在很简朴,只须要定义一个罗列类,示意序列化的算法,然后对 Packet 增添一个 algorithm 要领用来示意,运用何种序列化算法,以下所示:
public enum SerializeAlgorithm { JDK((byte) 1), FAST_JSON((byte) 2), HESSIAN((byte) 3), KRYO((byte) 4), PROTO_STUFF((byte) 5); private byte type; SerializeAlgorithm(byte type) { this.type = type; } } public abstract class Packet implements Serializable { public abstract byte algorithm(); }
然后定义一个 SerializerChooser 依据差别的算法挑选差别的 Serializer 完成类即可:
public interface SerializerChooser { Serializer choose(byte algorithm); }
由于依据算法是可以晓得对应的序列化接口的,所以就没有必要去扫描了,直接把几种序列化的完成类罗列出来即可,对象的实例可以运用单例形式,以下所示:
public class DefaultSerializerChooser implements SerializerChooser { private DefaultSerializerChooser() { } public static SerializerChooser getInstance() { return Singleton.get(DefaultSerializerChooser.class); } @Override public Serializer choose(byte algorithm) { SerializeAlgorithm serializeAlgorithm = SerializeAlgorithm.getEnum(algorithm); switch (serializeAlgorithm) { case JDK: { return Singleton.get(JdkSerializer.class); } case FAST_JSON: { return Singleton.get(FastJsonSerializer.class); } case HESSIAN: { return Singleton.get(HessianSerializer.class); } case KRYO: { return Singleton.get(KryoSerializer.class); } case PROTO_STUFF: { return Singleton.get(ProtoStuffSerializer.class); } default: { return null; } } } }
我说完后,人人又一次陷入了寻思,我晓得人人都在思索,他们会在每一次思索中取得提高和生长,正如我在思索后获得生长一样。
小鸟总有一天会生长为老鸟,我还走在生长的路上。
以上就是Java 怎样更好地治理你的对象的细致内容,更多请关注ki4网别的相干文章!