做者:Greyhtml
原文地址:java
語雀git
博客園github
Java SE 提供了三種方式,能夠實現IoC,分別爲:spring
java.beans包下的 Introspector 類提供了一個 getBeanInfo的方法,能夠獲取一個類的信息編程
BeanInfo bi=Introspector.getBeanInfo(User.class,Object.class);
如上,則能夠獲取User類對象的BeanInfo, 而後咱們經過BeanInfo中的 getPropertyDescriptors 方法,能夠獲取到User對象中的全部屬性和方法,json
注意:java beans中,對於set(xxx)方法,統一叫:writeMethod(), 對於get() 方法,統一叫:readMethod()微信
Stream.of(bi.getPropertyDescriptors()).forEach(pd->{ Class<?> propertyType=pd.getPropertyType(); Method writeMethod=pd.getWriteMethod(); });
獲取到方法和屬性名稱後,經過反射便可把對應的值設置到對應的屬性中ide
writeMethod.invoke(name,value);
因爲咱們注入屬性值的時候,咱們注入的東西永遠是一個字符串類型,若是須要注入的屬性是其餘類型(非字符串), 好比User類中,有一個屬性是address,這個address是一個對象類型,函數
咱們應該如何定義一個轉換器,將字符串類型的值轉換爲咱們須要的對象類型呢?
咱們須要經過設置一個AddressEditor來實現這個轉換,這個AddressEditor有兩種實現方式, 一是實現PropertyEditor接口,另一種方式是繼承PropertyEditorSupport類,
因爲咱們只須要實現一些簡單的轉換,PropertyEditorSupport提供了更爲便利的實現方式,因此咱們採用繼承PropertyEditorSupport類的方法,來實現類型的轉換,
Address類的設計是:
public class Address { private String name; private Integer num; // 省略 get / set / toString }
咱們的定義的規則以下,
輸入的字符串用|來分割 name 和 num屬性
例如: 「貝克街|221」 這個字符串 會將「貝克街」賦給name,221賦給num
public class AddressEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { String[] tokens = text.split("\\|"); Address address = new Address(); address.setName(tokens[0]); address.setNum(Integer.valueOf(tokens[1])); setValue(address); } }
可是咱們須要重寫setAsText方法,即:將字符串類型按照咱們定義的規則轉換成對應須要的類型便可,同理,咱們能夠實現一個DateEditor,讓「yyyy-MM-dd」這樣類型的字符串轉換成日期格式。
public class DateEditor extends PropertyEditorSupport { static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @Override public void setAsText(String text) throws IllegalArgumentException { LocalDate localDate = LocalDate.parse(text, dtf); ZoneId zone = ZoneId.systemDefault(); Instant instant = localDate.atStartOfDay().atZone(zone).toInstant(); setValue(Date.from(instant)); } }
而後,咱們須要使用java beans中的PropertyEditorManager類的registerEditor方法把這兩個Editor註冊進來
registerEditor(Address.class,AddressEditor.class); registerEditor(Date.class,DateEditor.class);
最後,PropertyEditorManager的findEditor方法就能夠根據咱們前面獲得的屬性類型,找到對應的Editor來對值進行轉換,轉換成咱們須要的屬性類型的值
PropertyEditor editor = findEditor(propertyType); if (editor != null) { editor.setAsText(parameters.get(pd.getName())); try { writeMethod.invoke(user, editor.getValue()); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } } else { System.out.println("no editor for:" + pd.getName()); }
主函數調用示例
public static void main(String[] args) throws Exception { Map<String, String> parameters = new HashMap<String, String>() { { //這裏的key要和Node裏面的屬性名一致 put("name", "福爾摩斯"); put("address", "貝克街|221"); put("birthday", "1854-01-06"); } }; User convert = PropertyEditorSample.convert(parameters); System.out.println(convert); }
運行結果
User{name='福爾摩斯', birthday=Thu Jan 05 23:54:17 CST 1854, address=Address{name='貝克街, 221 號}}
定義支付接口PayService
public interface PayService { void pay(); }
定義多個實現:
public class WeixinpayService implements PayService{ @Override public void pay() { System.out.println("微信支付"); } }
public class AlipayService implements PayService{ @Override public void pay() { System.out.println("支付寶支付"); } }
在resources目錄下創建META-INF文件夾,在META-INF文件夾下創建services目錄,同時創建一個文件,名稱爲接口的全路徑名,以這個項目爲例, PayService的全路徑名稱爲:
org.snippets.ioc.java.spi.PayService
在這個文件內,把實現類的全路徑名寫進去:
org.snippets.ioc.java.spi.AlipayService org.snippets.ioc.java.spi.WeixinpayService
客戶端調用:
ServiceLoader<PayService> serviceLoader = ServiceLoader.load(PayService.class); for (PayService ele : serviceLoader) { ele.pay(); }
其中ServiceLoader.load方法能夠把全部配置的PayService實現獲得
執行結果:
支付寶支付 微信支付
定義一個Person類
public class Person implements Remote, Serializable { private static final long serialVersionUID = 1L; private String name; private String password; // 省略set / get方法 }
實現JNDI的客戶端,實現初始化Person和查找Person兩個功能
public static void initPerson() throws Exception { //配置JNDI工廠和JNDI的url和端口。若是沒有配置這些信息,將會出現NoInitialContextException異常 LocateRegistry.createRegistry(3000); System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); System.setProperty(Context.PROVIDER_URL, "rmi://localhost:3000"); InitialContext ctx = new InitialContext(); //實例化person對象 Person p = new Person(); p.setName("zc"); p.setPassword("123"); //將person對象綁定到JNDI服務中,JNDI的名字叫作:person。 ctx.bind("person", p); ctx.close(); } public static void findPerson() throws Exception { //由於前面已經將JNDI工廠和JNDI的url和端口已經添加到System對象中,這裏就不用在綁定了 InitialContext ctx = new InitialContext(); //經過lookup查找person對象 Person person = (Person) ctx.lookup("person"); //打印出這個對象 System.out.println(person.toString()); ctx.close(); }